将JSON文档阅读为
Map
并进行比较您可以将两个JSON文档都读为
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);
然后使用Guava的
Maps.difference(Map<K, V>, Map<K, V>)
比较它们。它返回一个
MapDifference<K, V>
MapDifference<String, Object> difference = Maps.difference(leftMap, rightMap);
如果您对结果不满意,则可以考虑flanting them them the Maps,然后比较它们。它将提供更好的比较结果,尤其是对于嵌套对象和数组。
为比较创建扁平
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);
}
}
使用keSSON指针符号在
RFC6901示例
{
"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"
]
}
它将产生以下输出: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)
创建JSON补丁文档在
其他答案
中所描述的方法上,您可以将javaapi用于jsonProcessing在
JSR374
中定义的JSONPROCESTING(它在Gson或Jackson上不使用)。需要以下依赖项:
的定义:
JsonPatch diff = Json.createDiff(source, target);
当应用于源文档时,JSON补丁会产生目标文档。 JSON补丁可以使用以下方式应用于源文档。
JsonObject patched = diff.apply(source);
RFC7396:
JsonMergePatch mergeDiff = Json.createMergeDiff(source, target);
当应用于源文档时,JSON合并补丁会产生目标文档。要修补源,请使用:
JsonValue patched = mergeDiff.apply(source);
公文json文档
System.out.println(format(diff.toJsonArray()));
System.out.println(format(mergeDiff.toJsonValue()));
public static String format(JsonValue json) {
StringWriter stringWriter = new StringWriter();
prettyPrint(json, stringWriter);
return stringWriter.toString();
}
public static void prettyPrint(JsonValue json, Writer writer) {
Map<String, Object> config =
Collections.singletonMap(JsonGenerator.PRETTY_PRINTING, true);
JsonWriterFactory writerFactory = Json.createWriterFactory(config);
try (JsonWriter jsonWriter = writerFactory.createWriter(writer)) {
jsonWriter.write(json);
}
}
示例考虑以下JSON文档:
{
"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"
]
}
以及以下代码生成JSON补丁:
JsonValue source = Json.createReader(new StringReader(leftJson)).readValue();
JsonValue target = Json.createReader(new StringReader(rightJson)).readValue();
JsonPatch diff = Json.createDiff(source.asJsonObject(), target.asJsonObject());
System.out.println(format(diff.toJsonArray()));
它将产生以下输出:[
{
"op": "replace",
"path": "/name/first",
"value": "Jane"
},
{
"op": "add",
"path": "/name/nickname",
"value": "Jenny"
},
{
"op": "remove",
"path": "/address"
},
{
"op": "replace",
"path": "/birthday",
"value": "1990-01-01"
},
{
"op": "remove",
"path": "/company"
},
{
"op": "replace",
"path": "/occupation",
"value": null
},
{
"op": "replace",
"path": "/phones/1/number",
"value": "111111111"
},
{
"op": "remove",
"path": "/phones/0"
},
{
"op": "add",
"path": "/favorite",
"value": true
},
{
"op": "add",
"path": "/groups",
"value": [
"close-friends",
"gym"
]
}
]
现在考虑以下代码来产生JSON合并补丁:
JsonValue source = Json.createReader(new StringReader(leftJson)).readValue();
JsonValue target = Json.createReader(new StringReader(rightJson)).readValue();
JsonMergePatch mergeDiff = Json.createMergeDiff(source, target);
System.out.println(format(mergeDiff.toJsonValue()));
它将产生以下输出:
{
"name": {
"first": "Jane",
"nickname": "Jenny"
},
"address": null,
"birthday": "1990-01-01",
"company": null,
"occupation": null,
"phones": [
{
"number": "111111111",
"type": "mobile"
}
],
"favorite": true,
"groups": [
"close-friends",
"gym"
]
}
应用patches时不同的结果
应用补丁文档时,对于上述方法,结果略有不同。考虑将JSON补丁应用于文档的以下代码:
JsonPatch diff = ...
JsonValue patched = diff.apply(source.asJsonObject());
System.out.println(format(patched));
产生:
{
"name": {
"first": "Jane",
"last": "Doe",
"nickname": "Jenny"
},
"birthday": "1990-01-01",
"occupation": null,
"phones": [
{
"number": "111111111",
"type": "mobile"
}
],
"favorite": true,
"groups": [
"close-friends",
"gym"
]
}
现在考虑将JSON合并补丁程序应用于文档的以下代码:
JsonMergePatch mergeDiff = ...
JsonValue patched = mergeDiff.apply(source);
System.out.println(format(patched));
{
"name": {
"first": "Jane",
"last": "Doe",
"nickname": "Jenny"
},
"birthday": "1990-01-01",
"phones": [
{
"number": "111111111",
"type": "mobile"
}
],
"favorite": true,
"groups": [
"close-friends",
"gym"
]
}
在第一个示例中,occupation
null
。在第二个示例中,它被省略了。这是由于JSON Merge Patch上的null
RFC7396:
如果目标确实包含构件,则更换值。 合并贴片中的空值具有特殊的含义,以指示删除目标中现有值。 [...]
该设计意味着合并补丁文档适合描述主要使用对象进行结构的JSON文档的修改,并且不使用显式零值。 合并补丁格式不适合所有JSON语法。
您可以尝试我的图书馆-Yjson -delta.
。 它基于Gson,可以使用特定目的进行配置,例如忽略字段或不考虑缺失/意外字段。 示例
expected: { "type": "animal", "info": { "id": 123, "subtype": "Cat", "timestamp": 1684852390 } } actual: { "type": "animal", "info": { "id": 123, "subtype": "Tiger", "timestamp": 1684852399 } }
比较和印刷结果:
输出看起来像这样:
Status: failed
Mismatches:
"root.info.subtype": Value mismatch. Expected: "Cat"; Actual: "Tiger"
jSondeltareport
Success
(布尔值):比较结果(如果JSON相等的话,成功)mismatches
(列表):所有不匹配列表
您也可以使用javers
。示例:
private void assertOutputIsEquivalentToInput(String inputEvent, ObjectNode expectedJson,
JsonNode actualJson) {
var javers = JaversBuilder.javers().build();
var description=javers.compare(expectedJson, actualWithoutNulls).prettyPrint();
assertThat(description,actualJson,is(equalTo(expectedJson)));
}