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.
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.
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.
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.
Map
s and comparing themYou 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.
Map
s for the comparisonTo 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.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With