2

What's the best way to compare two JSON strings in Java? I want to be able to print out only the difference in key/values pairs.

I'm using gson library to convert it to a map and do assert but it displays both JSONs:

Type type = new TypeToken<Map<String, String>>() {}.getType();
Gson gson = new Gson();
Map<String, String> mapExpected = gson.fromJson(expectedJson, type);
Map<String, String> mapResponse = gson.fromJson(jsonResponse, type);
Assert.assertEquals(mapExpected, mapResponse);

Is there a better way to do this?

ypa
  • 73
  • 1
  • 6
  • Compute the [set difference][1]. [1]: http://stackoverflow.com/questions/1723168/what-is-the-fastest-or-most-elegant-way-to-compute-a-set-difference-using-javascr/1723220#1723220 – mcandre Aug 20 '11 at 01:12
  • @mcandre, your link is about JS, he asks in java – SJuan76 Aug 20 '11 at 01:20
  • The concept is still applicable. Convert the JSON data into sets, then display the difference (`-`). – mcandre Aug 20 '11 at 01:22
  • 1
    I agree with the basic idea. Still IMHO the post still is not applicable as JSON structure is a map and not a set, an intermediate pass is needed to do the conversion. – SJuan76 Aug 20 '11 at 01:29

3 Answers3

4

If you just wanted to compare simple equality, Jackson would make it easy; and just in case this might help it'd be:

JsonNode tree1 = objectMapper.readTree(json1);
JsonNode tree2 = objectMapper.readTree(json2);
if (!tree1.equals(tree2)) { // handle diffing
}

Now; since all JsonNode objects properly implement equality checks (so that ordering of keys does not matter etc), you could use this for recursive diffing to see where and how contents differ. For ObjectNodes you could get key names, remove same (tree1.getFieldNames().removeAll(tree2.getFieldNames()) and so on.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
2

It is tricky, but it can be done.

I would implement a Pair class that holds both String (key and value), with its corresponding equals() and hashcode();

Then put all elements from Map A as Pair in a Set (setA) and all elements from Map B in another Set (setB)

Then calculate

 Set<Pair> setA_B = setA.removeAll(setB);
 Set<Pair> setB_A = setB.removeAll(setA);
 Set<Pair> result = setA_B.addAll(setB_A);

In result there are only the elements that do not match. If all elements match (both original maps are equal) then result is empty.

Amol M Kulkarni
  • 21,143
  • 34
  • 120
  • 164
SJuan76
  • 24,532
  • 6
  • 47
  • 87
  • Thanks SJuan76, for the pointer. It works, I used AbstractMap's SimpleEntry for Pair. – ypa Aug 22 '11 at 17:37
2

Using SJuan76's solution, I was able to get just the difference in Key value pairs.

    Type type = new TypeToken<Map<String, String>>() {}.getType();
    Gson gson = new Gson();
    Map<String, String> mapExpected = gson.fromJson(expectedJson, type);
    Map<String, String> mapResponse = gson.fromJson(jsonResponse, type);
    Set<SimpleEntry<String,String>> expectedSet = new HashSet<SimpleEntry<String, String>>();
    Set<SimpleEntry<String, String>> tmpExpectedSet = new HashSet<SimpleEntry<String, String>>();
    Set<SimpleEntry<String, String>> responseSet = new HashSet<SimpleEntry<String, String>>();

    for (String key : mapExpected.keySet()) {
        expectedSet.add(new SimpleEntry<String, String>(key, mapExpected.get(key)));
        tmpExpectedSet.add(new SimpleEntry<String, String>(key, mapExpected.get(key)));
    }

    for (String key : mapResponse.keySet())
        responseSet.add((new SimpleEntry<String, String>(key, mapResponse.get(key))));

    expectedSet.removeAll(responseSet);
    responseSet.removeAll(tmpExpectedSet);
    expectedSet.addAll(responseSet);

    if (!expectedSet.isEmpty()) {
        for (SimpleEntry<String, String> diff : expectedSet)
            log.error(diff.getKey() + ":" + diff.getValue());
    }
ypa
  • 73
  • 1
  • 6