如何比较JSON文档并返回与Jackson或Gson的差异?

iyfamqjs  于 2022-11-06  发布在  其他
关注(0)|答案(2)|浏览(142)

我正在使用spring-boot来开发后端服务。有一个场景比较2-beans(一个是DB对象,另一个是客户端请求的对象),并返回“new element”,“modified element”,如果没有变化,则返回false。2-beans的格式如下

"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"
           }
        }
     ]
  }

我已经浏览了互联网上的bean比较在java中的这个场景,但我不能找到任何简单的解决方案,但找到了一些很酷的JSON解决方案。我可以看到一些解决方案的GSON,但它不会返回客户端对象包含“新元素”和“更改元素”。有没有什么办法返回更新和修改的元素在JSON或JAVA中?你的帮助应该是值得赞赏的。即使是一个暗示对我来说也是一个很好的开始。

93ze6v8z

93ze6v8z1#

Map的形式阅读JSON文档并进行比较

您可以将这两个JSON文档都读作Map<K, V>。请参见下面的Jackson和Gson示例:
第一个
然后使用Guava的Maps.difference(Map<K, V>, Map<K, V>)来比较它们,它返回一个MapDifference<K, V>示例:

MapDifference<String, Object> difference = Maps.difference(leftMap, rightMap);

如果您对结果不满意,可以考虑展平Map,然后进行比较。这将提供更好的比较结果,尤其是对于嵌套对象和数组。

正在创建平面Map以进行比较

要展开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);
    }
}

它使用在RFC 6901中定义的 *JSON指针表示法 * 作为键,因此您可以轻松地找到值。

示例

请考虑以下JSON文档:
第一个
和下面的代码来比较它们并显示差异:

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)
xienkqul

xienkqul2#

创建JSON修补程序文档

除了other answer中描述的方法之外,您还可以使用JSR 374中定义的Java API for JSON Processing(Gson或Jackson上不使用它)。需要以下依赖项:

<!-- Java API for JSON Processing (API) -->
<dependency>
    <groupId>javax.json</groupId>
    <artifactId>javax.json-api</artifactId>
    <version>1.1.2</version>
</dependency>

<!-- Java API for JSON Processing (implementation) -->
<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.json</artifactId>
    <version>1.1.2</version>
</dependency>

然后,您可以从JSON文档创建一个JSON diff,它将生成一个RFC 6902

JsonPatch diff = Json.createDiff(source, target);

当套用至来源文件时,JSON Patch会产生目的文件。JSON Patch可以使用下列方式套用至来源文件:

JsonObject patched = diff.apply(source);

创建JSON合并修补程序文档

根据您的需要,您可以创建一个JSON合并补丁文档,如RFC 7396

JsonMergePatch mergeDiff = Json.createMergeDiff(source, target);

套用至来源文件时,“JSON合并修正程式”会产生目的文件。若要修正来源文件,请用途:

JsonValue patched = mergeDiff.apply(source);

漂亮地打印JSON文档

要漂亮地打印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"
    ]
}

应用修补程序时的不同结果

当应用补丁文档时,上述方法的结果会稍有不同。请考虑以下将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合并补丁上的null语义。从RFC 7396
如果目标中确实包含该成员,则替换该值。合并补丁中的空值具有特殊含义,表示删除目标中的现有值。[...]
这种设计意味着合并补丁文档适用于描述对JSON文档的修改,这些文档主要使用对象作为其结构,并且不使用显式空值。合并补丁格式并不适用于所有JSON语法。

相关问题