Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to compare JSON documents and return the differences with Jackson or Gson?

I am using spring-boot to develop backend services. There is a scenario to compare 2-beans(one is the DB object and another one is the client requested object) and return the "new element","modified element" and if there is no change then return false. The 2-beans are in a below format

"sampleList":{      "timeStamp":"Thu, 21 Jun 2018 07:57:00 +0000",      "id":"5b19441ac9e77c000189b991",      "sampleListTypeId":"type001",      "friendlyName":"sample",      "contacts":[         {            "id":"5b05329cc9e77c000189b950",            "priorityOrder":1,            "name":"sample1",            "relation":"Friend",            "sampleInfo":{               "countryCode":"91",               "numberType":"MOBILE",               "numberRegion":"IN"            }         },         {            "id":"5b05329cc9e77c000189b950",            "priorityOrder":1,            "name":"sample2",            "relation":"Friend",            "sampleInfo":{               "countryCode":"91",               "numberType":"MOBILE",               "numberRegion":"IN"            }         }      ]   } 

I have browsed internet about bean comparison for this scenario in java but I couldn't find any simpler solution but found some cool solution for JSON. I can see some solution for GSON but it will not return the client object contains "new element" and the "changes element". Is there any way to return the newer and modified element in JSON or JAVA? Your help should be appreciable. Even a hint will be a great start for me.

like image 793
VelNaga Avatar asked Jun 21 '18 11:06

VelNaga


People also ask

How do I tell the difference between two JSON files?

You can also directly compare two JSON files by specifying their urls in the GET parameters url1 and url2. Then you can visualize the differences between the two JSON documents. It highlights the elements which are different: Different value between the two JSON: highlight in red color.

What is difference between JSON and GSON?

The JSON format was originally specified by Douglas Crockford. On the other hand, GSON is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to an equivalent Java object.

Can we compare two JSON objects?

Similarly, we can also compare two JSON objects that contain a list element. It's important to know that two list elements are only compared as equal if they have the same values in the exact same order.


1 Answers

Reading the JSON documents as Maps and comparing them

You could read both JSON documents as Map<K, V>. See the below examples for Jackson and Gson:

ObjectMapper mapper = new ObjectMapper(); TypeReference<HashMap<String, Object>> type =      new TypeReference<HashMap<String, Object>>() {};  Map<String, Object> leftMap = mapper.readValue(leftJson, type); Map<String, Object> rightMap = mapper.readValue(rightJson, type); 
Gson gson = new Gson(); Type type = new TypeToken<Map<String, Object>>(){}.getType();  Map<String, Object> leftMap = gson.fromJson(leftJson, type); Map<String, Object> rightMap = gson.fromJson(rightJson, type); 

Then use Guava's Maps.difference(Map<K, V>, Map<K, V>) to compare them. It returns a MapDifference<K, V> instance:

MapDifference<String, Object> difference = Maps.difference(leftMap, rightMap); 

If you are not happy with the result, you can consider flattening the maps and then compare them. It will provide better comparison results especially for nested objects and arrays.

Creating flat Maps for the comparison

To flat the map, you can use:

public final class FlatMapUtil {      private FlatMapUtil() {         throw new AssertionError("No instances for you!");     }      public static Map<String, Object> flatten(Map<String, Object> map) {         return map.entrySet().stream()                 .flatMap(FlatMapUtil::flatten)                 .collect(LinkedHashMap::new, (m, e) -> m.put("/" + e.getKey(), e.getValue()), LinkedHashMap::putAll);     }      private static Stream<Map.Entry<String, Object>> flatten(Map.Entry<String, Object> entry) {          if (entry == null) {             return Stream.empty();         }          if (entry.getValue() instanceof Map<?, ?>) {             return ((Map<?, ?>) entry.getValue()).entrySet().stream()                     .flatMap(e -> flatten(new AbstractMap.SimpleEntry<>(entry.getKey() + "/" + e.getKey(), e.getValue())));         }          if (entry.getValue() instanceof List<?>) {             List<?> list = (List<?>) entry.getValue();             return IntStream.range(0, list.size())                     .mapToObj(i -> new AbstractMap.SimpleEntry<String, Object>(entry.getKey() + "/" + i, list.get(i)))                     .flatMap(FlatMapUtil::flatten);         }          return Stream.of(entry);     } } 

It uses the JSON Pointer notation defined in the RFC 6901 for the keys, so you can easily locate the values.

Example

Consider the following JSON documents:

{   "name": {     "first": "John",     "last": "Doe"   },   "address": null,   "birthday": "1980-01-01",   "company": "Acme",   "occupation": "Software engineer",   "phones": [     {       "number": "000000000",       "type": "home"     },     {       "number": "999999999",       "type": "mobile"     }   ] } 
{   "name": {     "first": "Jane",     "last": "Doe",     "nickname": "Jenny"   },   "birthday": "1990-01-01",   "occupation": null,   "phones": [     {       "number": "111111111",       "type": "mobile"     }   ],   "favorite": true,   "groups": [     "close-friends",     "gym"   ] } 

And the following code to compare them and show the differences:

Map<String, Object> leftFlatMap = FlatMapUtil.flatten(leftMap); Map<String, Object> rightFlatMap = FlatMapUtil.flatten(rightMap);  MapDifference<String, Object> difference = Maps.difference(leftFlatMap, rightFlatMap);  System.out.println("Entries only on the left\n--------------------------"); difference.entriesOnlyOnLeft()           .forEach((key, value) -> System.out.println(key + ": " + value));  System.out.println("\n\nEntries only on the right\n--------------------------"); difference.entriesOnlyOnRight()           .forEach((key, value) -> System.out.println(key + ": " + value));  System.out.println("\n\nEntries differing\n--------------------------"); difference.entriesDiffering()           .forEach((key, value) -> System.out.println(key + ": " + value)); 

It will produce the following output:

Entries only on the left -------------------------- /address: null /phones/1/number: 999999999 /phones/1/type: mobile /company: Acme   Entries only on the right -------------------------- /name/nickname: Jenny /groups/0: close-friends /groups/1: gym /favorite: true   Entries differing -------------------------- /birthday: (1980-01-01, 1990-01-01) /occupation: (Software engineer, null) /name/first: (John, Jane) /phones/0/number: (000000000, 111111111) /phones/0/type: (home, mobile) 
like image 61
cassiomolin Avatar answered Sep 23 '22 01:09

cassiomolin