This is a library that will help you map one JSON to another JSON.
Many times in our code we map one rest Api out put to our data class, which is basically mapping from one JSON to another.
Errors happen during these mappings,
and sometimes the mappings are obvious still we need to add those codes transforming one JSON to another.
This library tries to externalize these JSON to JSON mapping.
Where you provide the input to output mappings in a JSON file and library will create outJson from input JSON using the mapping json.
It may feel that code is being transferred from classes to JsonMapper.
That's somewhat true, but many advantages of this approach is
- duplication of logic is now avoided. e.g., get from x, set in y etc.
- declaring mappings beforehand makes the code more readable. making all mappings at single place.
- same mappings can be reused at multiple places if the input structure is used at multiple places.
Library is available to install via maven central
if using maven as build tool , use this in your dependencies
<!-- https://mvnrepository.com/artifact/io.github.vivek656/jsontojson -->
<dependency>
<groupId>io.github.vivek656</groupId>
<artifactId>jsontojson</artifactId>
<version>0.0.1-dev</version>
</dependency>
for gradle, sbt or other build tools see. Maven repository link HERE
Mapper.json is the file that you will add that contains mapping information how a path in input.json maps to output.json.
Suppose this is Input.json that we want to map to other JSON
{
"address": {
"postCode": "AXBHI",
"line1" : "789"
},
"uprn" : "6789",
"owners" : [
{
"name" : "viv",
"type" : "freehold"
}
],
"name" : {
"fn" : "vivek",
"ln": "jacob",
"mn": "singh"
}
}
Mapper can be like this
{
"mappings": {
"/address/postCode": {
"pathToMap": "/address_postcode"
},
"/address/line1": {
"pathToMap": "/address_line1"
},
"/uprn": {
"pathToMap": "/uprn"
},
"/owners/-/name": {
"pathToMap": "/owner/name"
},
"/owners/--/type": {
"pathToMap": "/ownerTypes"
}
},
"mappings_complex": [
{
"operationType": "function",
"pathToMap": "/name/full_name",
"mapping_spec": {
"function_body": {
"function_name": "concat",
"args": {
"values": [
{
"function_name": "uppercase",
"args": {
"value": {
"path": "/name/fn"
}
}
},
{
"path": "/name/mn"
},
{
"path": "/name/ln"
}
],
"separator": " "
}
}
}
}
]
}
in this JSON mappings contains all the straight forward mappings from input keys to output keys.
Mapping to output json path
you need to provide a key path from root
e.g. /address_line1
add data at root.address_line1
while /address/line1
will mean add data at root.address.line
.
example of one such mapping will be described here
/address/postcode
is the key path in input.json.
pathToMap
tells the path in outputJson which is /address_postcode
in this case
complex mappings is where we can do transformation on input keys before mapping it to output json path.
- operationType
this field tells the parser the operation that we want to perform on input json before mapping. Currently onlyfunction
is supported - pathToMap
with one mapping input we can map to only one output json key, specified in path to map. - mapping_spec
Similar to k8s object specs, specs structure can vary based on an operation type.
As we can see specs can contain nested specs as well.
When we apply mapper.json to input.json we get output like this
{
"name" : {
"full_name" : "VIVEK singh jacob"
},
"address_postcode" : "AXBHI",
"owner" : {
"name" : "viv"
},
"address_line1" : "789",
"uprn" : "6789",
"ownerTypes" : [ "freehold" ]
}
One common rule is keys mostly follows JSON Pointer convention except some special cases like arrays. see the JSON pointer rfc here
-
Simple JSON Path
path like/address/postCode
directly matched to postcode inside address in the root folder. -
Matching array item
- We can match array item by index (zero indexed)
like
/owners/0/name
this means match key traversal like this. -> owners -> 0th element -> name
- We can match array item by index (zero indexed)
like
-
Matching All array Items
- We can tell the parser that inside each array element grab one key and convert to a new array using
--
e.g./owners/--/type
we are saying maps each type field inside each array item in owners this programmatically meansowners.map{ a => a.type }
- We can tell the parser that inside each array element grab one key and convert to a new array using
the object { "path": "/path/to/field" }
is a special specs. that we can use to specify simple path mappings.
every function should have an args
field , which contain args to the function.
Below contains all supported functions
-
- as name suggest this concat the
values
with the providedseperator
default separator will be empty char "". values can itself be function or specs that resolves to a value.
- as name suggest this concat the
-
- uppercase will uppercase the
value
value
can be a function or any other spec.
- uppercase will uppercase the