10

I want to compare two JSON strings in Java 8 for equality, but ignore specific known nodes which contain values which are expected to be different and are a tolerated difference e.g. timestamp. Currently using JSONAssert v1.5.0 from org.SkyScreamer, I'm able to 'ignore' a number of these nodes, but cannot 'ignore' nodes within an array.

What I would like is to extend my current JSONComparator to include a Customization which has its first 'path' parameter having an array address.

JSONComparator customisedJobComparator = new CustomComparator(JSONCompareMode.NON_EXTENSIBLE,
            new Customization("id", (o1, o2) -> true),
            new Customization("appointmentBookedDate.$date", (o1, o2) -> true),
            ...
            new Customization("someArray[n].timestamp", (o1, o2) -> true) //<--- this is wrong, what is the correct way for this path address?
            );

The code below is my attempt at proving a solution; both expected/actual JSON strings are identical except for anArray[].id values. I want this test to pass, but is failing with error: java.lang.AssertionError: anArray[0] Could not find match for element {"id":"valueA"}

@Test
public void compareJsonStrIgnoringDiffInArray() {
    String errorMsg = "";
    String expectedJsonStr = "{\"anArray\": [{\"id\": \"valueA\"}, {\"colour\": \"Blue\"}]}";
    String actualJsonStr = "{\"anArray\": [{\"id\": \"valueB\"}, {\"colour\": \"Blue\"}]}";

    //Create custom comparator which compares two json strings but ignores reporting any differences for anArray[n].id values
    //as they are a tolerated difference
    Customization customization = new Customization("anArray[n].id", (o1, o2) -> true);
    JSONComparator customisedComparator = new CustomComparator(JSONCompareMode.NON_EXTENSIBLE, customization);

    JSONAssert.assertEquals(errorMsg, expectedJsonStr, actualJsonStr, customisedComparator);
}
Alex Shesterov
  • 26,085
  • 12
  • 82
  • 103
Mark H
  • 111
  • 1
  • 5

2 Answers2

19

After some digging in the javadoc for JSONAssert, I saw an example that used an array of objects. From that example I was able to modify your test case:

    @Test
    public void compareJsonStrIgnoringDiffInArray() throws JSONException {
        String errorMsg = "";
        String expectedJsonStr = "{\"anArray\": [{\"id\": \"valueA\"}, {\"colour\": \"Blue\"}]}";
        String actualJsonStr = "{\"anArray\": [{\"id\": \"valueB\"}, {\"colour\": \"Blue\"}]}";

        //Create custom comparator which compares two json strings but ignores reporting any differences for anArray[n].id values
        //as they are a tolerated difference

        ArrayValueMatcher<Object> arrValMatch = new ArrayValueMatcher<>(new CustomComparator(
                JSONCompareMode.NON_EXTENSIBLE,
                new Customization("anArray[*].id", (o1, o2) -> true)));

        Customization arrayValueMatchCustomization = new Customization("anArray", arrValMatch);
        CustomComparator customArrayValueComparator = new CustomComparator(
                JSONCompareMode.NON_EXTENSIBLE, 
                arrayValueMatchCustomization);
        JSONAssert.assertEquals(expectedJsonStr, actualJsonStr, customArrayValueComparator);
    }
D.B.
  • 4,523
  • 2
  • 19
  • 39
  • 1
    Thanks D.B. I've been trying your previously published attempts (I didn't want to do pre-processing of my JSON string if I could do it in the custom comparator somehow). gson.fromJson() -> gson.toJson() pre-processing worked of course, but i'm going to replace this with your above code (after testing it works). Thanks for your efforts, mate. – Mark H Jun 19 '18 at 05:37
  • 2
    Didn't see this anywhere else. Glad I bump into this. thanks – Isuru Jan 16 '19 at 15:08
3

In case of my requirements, need to ignore the fields businessCorrelationId present only once, effectiveStartDate present in each node of data.

{
  "messageElements": {
    "messageStatus": "SUCCESS",
    "businessCorrelationId": "a80337639eb40758",
    "errorList": []
  },
  "data": [
    {
      "referenceId": 1,
      "category": "CAT1",
      "subCategory": "SUB1",
      "effectiveStartDate": "2021-02-08T00:00:00",
      "activeFlg": true,
      "version": 1
    },
    {
      "referenceId": 2,
      "category": "CAT2",
      "subCategory": "SUB2",
      "effectiveStartDate": "2021-02-08T00:00:00",
      "effectiveEndDate": null,
      "activeFlg": true,
      "version": 1
    },
    {
      "referenceId": 3,
      "category": "CAT3",
      "subCategory": "SUB3",
      "effectiveStartDate": "2021-02-08T00:00:00",
      "activeFlg": true,
      "version": 1
    }
  ],
  "tenant": {
    "tenantId": null,
    "timeZoneOffset": null,
    "name": null
  }
}

Below code worked fine.

JSONAssert.assertEquals(expectedJson, actualJson, new CustomComparator(JSONCompareMode.LENIENT,
   new Customization("data[*].createdTimestamp", (ct1, ct2) -> true),
   new Customization("messageElements.businessCorrelationId", (c1, c2) -> true)));
Paramesh Korrakuti
  • 1,997
  • 4
  • 27
  • 39
  • This solution did not work for me ("data[*].createdTimestamp") , I had to have a ArrayValueMatcher. My array values are nested, though, as in : "response.results[*].date". – caytekin Feb 01 '22 at 10:42
  • mine is like this: response.results[].date[].something1.something2 How to ignore that "something2"??? – Mazin Ismail Sep 28 '22 at 16:57
  • 1
    @MazinIsmail Did you try with new `Customization("esponse.results[*].date[*].something1.something2, (p1, p2) -> true)` ? – Paramesh Korrakuti Sep 29 '22 at 04:57
  • @caytekin what is your response format and how you tried to ignore the paths? – Paramesh Korrakuti Sep 29 '22 at 04:58
  • @ParameshKorrakuti, in your example you used the wrong name for the `createdTimestamp`, because in the provided JSON the attribute is named `effectiveStartDate`. – belgoros Apr 17 '23 at 14:21
  • @ParameshKorrakuti if you run the code from the [gist](https://gist.github.com/belgoros/17bdfc20f78c84fb8d087e4fb5424c08) I created, you will see that it does not work. It works only for the attributes on the same level, like for `businessCorrelationId` value. – belgoros Apr 17 '23 at 14:40