programing

JSON 문서를 비교하고 Jackson 또는 Gson과의 차액을 반환하는 방법은 무엇입니까?

sourcejob 2023. 3. 5. 09:45
반응형

JSON 문서를 비교하고 Jackson 또는 Gson과의 차액을 반환하는 방법은 무엇입니까?

spring-boot을 사용하여 백엔드 서비스를 개발하고 있습니다.2-beans(하나는 DB 객체, 다른 하나는 클라이언트 요청 객체)를 비교하여 "new element", "modified element"를 반환하고 변경되지 않으면 false를 반환하는 시나리오가 있습니다.2개의 콩은 다음과 같은 형식입니다.

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

자바에서 이 시나리오의 콩 비교에 대해 인터넷을 찾아보았지만 간단한 해결책은 찾을 수 없었지만 JSON을 위한 멋진 솔루션을 찾았습니다.GSON의 솔루션은 표시되지만 "new element"와 "changes element"가 포함된 클라이언트 오브젝트는 반환되지 않습니다.JSON 또는 JAVA에서 새로운 수정 요소를 반환할 수 있는 방법이 있습니까?당신의 도움은 감사할만 합니다.힌트 하나라도 좋은 출발이 될 것 같아요.

JSON 문서 읽기Maps 및 비교하다

양쪽 JSON 문서를 다음과 같이 읽을 수 있습니다.Jackson과 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);

그런 다음 구아바의 것을 사용하여 비교합니다.인스턴스를 반환합니다.

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

결과에 만족하지 못할 경우 지도를 평평하게 만든 후 비교할 수 있습니다.특히 중첩된 개체와 배열에 대해 더 나은 비교 결과를 제공합니다.

평면 작성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 문서를 검토합니다.

{
  "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)

JSON 패치 문서 작성

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에 정의되어 있는JSON 패치 문서가 생성됩니다.

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

원본 문서에 적용되면 JSON 패치가 대상 문서를 생성합니다.JSON 패치는 다음을 사용하여 원본 문서에 적용할 수 있습니다.

JsonObject patched = diff.apply(source);

JSON 병합 패치 문서 작성

필요에 따라 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 패치를 작성하기 위한 다음 코드:

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 Merge Patch를 적용하는 다음 코드를 생각해 보겠습니다.

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두 번째 예에서는 생략되어 있습니다.★★★★ nullsemantics를 참조해 주세요.RFC 7396부터:

대상에 멤버가 포함되어 있으면 값이 바뀝니다.병합 패치의 null 값은 대상에서 기존 값이 제거되었음을 나타내는 특별한 의미가 있습니다. [...]

이 설계는 병합 패치 문서가 구조에 객체를 주로 사용하고 명시적 null 값을 사용하지 않는 JSON 문서에 대한 수정 사항을 설명하는 데 적합함을 의미합니다.병합 패치 형식이 모든 JSON 구문에 적합한 것은 아닙니다.

언급URL : https://stackoverflow.com/questions/50967015/how-to-compare-json-documents-and-return-the-differences-with-jackson-or-gson

반응형