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 th
Map
s and comparing themYou could read both JSON documents as Map
ObjectMapper mapper = new ObjectMapper();
TypeReference> type =
new TypeReference>() {};
Map leftMap = mapper.readValue(leftJson, type);
Map rightMap = mapper.readValue(rightJson, type);
Gson gson = new Gson();
Type type = new TypeToken
Then use Guava's Maps.difference(Map
MapDifference 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 flatten(Map map) {
return map.entrySet().stream()
.flatMap(FlatMapUtil::flatten)
.collect(LinkedHashMap::new, (m, e) -> m.put("/" + e.getKey(), e.getValue()), LinkedHashMap::putAll);
}
private static Stream> flatten(Map.Entry 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(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 leftFlatMap = FlatMapUtil.flatten(leftMap);
Map rightFlatMap = FlatMapUtil.flatten(rightMap);
MapDifference 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)