skip to Main Content

I have a program where I compare two response bodies, but I do not know what the bodies look like so instead of de-serializing them into Java objects I keep them as JsonNode objects. Suppose I have the following two JsonNode objects:

{
  "name": "Bob",
  "Age": 30,
  "id": 1,
  "status" : {
    "active": "true",
    "address": "123 Main St"
  }
}

and

{
  "name": "Bob",
  "Age": 30,
  "status" : {
    "active": "true"
  }
}

The second JsonNode object is missing the id and status.address fields. However, for the fields that exist for both JsonNode objects they are equal. When comparing them I want to ignore comparing missing fields, and just compare on the fields that exist in both JsonNode objects. Again, I cannot de-serialize them into objects and compare that way via Jackson annotations. So my constraints are:

  1. I do not know how the response bodies will look at runtime, but all the fields of the first JsonNode will appear in the second JsonNode, but the second JsonNode may have fields that dont exist in the first JsonNode.
  2. Compare the fields that appear in both JsonNode objects

Is there any way to achieve this with Jackson, and if so could someone point to the documentation for it? I would assume I would have to write my own comparator, but any existing literature on this would be helpful.

2

Answers


  1. You need to write custom comparison. One option is to use JsonNode.equals(Comparator, JsonNode) with custom comparator. But since you don’t care about ordering it may be better to go just for equality check. The example below uses recursion to check nodes depth-first. Depending on the actual use case you may need to switch to a breadth-first algorithm.

    public class CustomNodeComparator {
    
      public static boolean customEquals(JsonNode node1, JsonNode node2) {
        JsonNodeType node1Type = node1.getNodeType();
        if (!node1Type.equals(node2.getNodeType())) {
          //array and object are not equal
          return false;
        }
        Iterator<String> names = node1.fieldNames();
        while (names.hasNext()) {
          String name = names.next();
          JsonNode o2Node = node2.get(name);
          if (o2Node == null) {
            //assume equal, continue with other nodes
            continue;
          }
          JsonNode o1Node = node1.get(name);
          if (!o1Node.isValueNode()) {
            return customEquals(o1Node, o2Node);
          }
          //compares everything as text
          //depending on requirements you may need to compare taking value type into consideration
          boolean isEqual = o1Node.asText().equals(o2Node.asText());
          if (!isEqual) {
            //end fast, no need to compare the rest
            return false;
          }
        }
        return true;
      }
    }
    

    Note that the example does not take into consideration possibility of comparing two arrays.

    Login or Signup to reply.
  2. You may try library Josson & Jossons to compare the two JSON datasets by operator:

    Subtract right from left <-< or
    Subtract left from right >->

    It handles array comparison as well.

    https://github.com/octomix/josson

    Jossons jossons = new Jossons();
    jossons.putDataset("json1", Josson.fromJsonString(
        "{" +
        "  "name": "Bob"," +
        "  "Age": 30," +
        "  "id": 1," +
        "  "status" : {" +
        "    "active": "true"," +
        "    "array1": [1,2,3]," +
        "    "array2": [4,5]," +
        "    "address": "123 Main St"" +
        "  }" +
        "}"));
    jossons.putDataset("json2", Josson.fromJsonString(
        "{" +
        "  "name": "Bob"," +
        "  "Age": 30," +
        "  "status" : {" +
        "    "array1": [3,2,1]," +
        "    "array2": [6,5]," +
        "    "active": "true"" +
        "  }" +
        "}"));
    System.out.println(jossons.evaluateQuery("json1 <-< json2").toPrettyString());
    

    Output

    {
      "id" : 1,
      "status" : {
        "array2" : [ 4 ],
        "address" : "123 Main St"
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search