Reading the JSON documents as Map
s 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);
然后用番石榴的Maps.difference(Map<K, V>, Map<K, V>)
来比较它们.它返回一个MapDifference<K, V>
实例:
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 Map
s for the comparison
要展开 map ,可以使用:
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"
]
}
和下面的代码来比较它们并显示差异:
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));
它将产生以下输出:
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)