Search code examples
javajsontemplatesconfiguration

How to template JSON in java


My Use case is I have a json file but I have to share only few of them to client. Ex: Consider the source json file as shown below.

{
    "name": "XYZ",
    "age": 24,
    "education": {
        "college": "ppppp",
        "study": "b.tech",
        "grade": 6.8
    },
    "friends": ["kkkk",
    "bbbbbbbbbbb",
    "jjjjjj"],
    "dob":"01-08-1990"
}

For client 1 I have to share below output

{
    "personalInfo": {
        "name": "XYZ",
        "age": 24,
        "friendsNames": ["kkkk","bbbbbbbbbbb","jjjjjj"]
    },
    "educationalInfo": {
        "college": "ppppp",
        "study": "b.tech",
        "grade": 6.8
    }
}

For client 2 I have to share below output

{
    "personalInformation": {
        "nameOfEmployee": "XYZ",
        "ageOfEmployee": 24
    },
    "educationalInformation": {
        "college": "ppppp",
        "study": "b.tech"
    }
}

And for other clients also the use case is same, I have to skip some keys and give different names to the keys. How to dynamically do this by some kind of configuration. I used jsonPath to achieve this but removing few keys from json object is difficult. Any suggestions can be appreciated.


Solution

  • Use a JSON Path library, like JayWay.

    Then the 2 templates to transform your source JSON would be:

    • client 1:

      {
          "personalInfo": {
              "name": "$.name",
              "age": "$.age",
              "friendsNames": "$.friends"
          },
          "educationalInfo": "$.education"
      }
      
    • client 2:

      {
          "personalInformation": {
              "nameOfEmployee": "$.name",
              "ageOfEmployee": "$.age"
          },
          "educationalInformation": {
              "college": "$.education.college",
              "study": "$.education.study"
          }
      }
      

    What you need to implement is the recursive template traversal via the JayWay's MapFunction:

    {
        ReadContext source = JsonPath.parse(...);
        MapFunction mapper = mapFunction(source);
    
        WriteContext template1 = JsonPath.parse(...);
        WriteContext template2 = JsonPath.parse(...);
    
        String client1 = template1.map("*", mapper).jsonString();
        String client2 = template2.map("*", mapper).jsonString();
    }
    
    MapFunction mapFunction(ReadContext source) {
        return (templateNode, __) -> switch (templateNode) {
            case Map map -> recurse(map, mapFunction(source));
            case List list -> recurse(list, mapFunction(source));
            case String path -> source.read(path);
        };
    }
    
    <T, R> R recurse(T templateNode, MapFunction mapper) {
        return JsonPath.parse(templateNode).map("*", mapper).read("$");
    }
    

    Optionally, if you also need to transform each element of a list of variable length, using the same sub-template for each element, extend the case List list branch of the mapFunction() to support a template syntax like:

    {
        "targetProperty": [ "$.source.path.to.an.array", {
            ... element template ...
        } ]
    }
    

    E.g. a template like:

    {
        "friends": [ "$.friends[?(@ =~ /.{5,}/)]", {
            "name": "@",
            "since": "$.dob"
        } ]
    }
    

    would result into:

    {
        "friends": [ {
            "name": "bbbbbbbbbbb",
            "since": "01-08-1990"
        }, {
            "name": "jjjjjj",
            "since": "01-08-1990"
        } ]
    }