创建一个JSON Patch文档
除了在其他答案中描述的方法,您还可以使用在JSR 374中定义的Java JSON处理API(它不使用Gson或Jackson)。需要以下依赖项:
<dependency>
<groupId>javax.json</groupId>
<artifactId>javax.json-api</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<version>1.1.2</version>
</dependency>
接下来,您可以从JSON文档创建一个JSON差异。它将生成一个JSON Patch文档,如RFC 6902所定义:
JsonPatch diff = Json.createDiff(source, target);
在应用到源文档时,JSON Patch 将产生目标文档。可以使用以下方法将 JSON Patch 应用于源文档:
JsonObject patched = diff.apply(source);
创建 JSON Merge Patch 文档
根据您的需求,您可以创建一个符合 RFC 7396 定义的 JSON Merge Patch 文档:
JsonMergePatch mergeDiff = Json.createMergeDiff(source, target);
应用JSON合并补丁到源文档后,将得到目标文档。要对源进行打补丁,请使用:
JsonValue patched = mergeDiff.apply(source);
美化JSON文档
要美化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 Patch:
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"
]
}
应用补丁时产生不同的结果
应用补丁文档后,使用上述方法得到的结果略有不同。请考虑以下应用 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
语义。来自
RFC 7396:
如果目标包含该成员,则该值会被替换。合并补丁中的null值具有特殊含义,可用于指示删除目标中现有值。[...]
此设计意味着合并补丁文档适用于描述主要使用对象进行结构化的JSON文档的修改,并且不使用显式null值。合并补丁格式并不适用于所有JSON语法。
.filter()
来删除你不想比较的键,或者直接从比较结果中删除该键。但这超出了你原始问题的范围。 - cassiomolin