라이브러리

JsonDiff vs MapDifference

탄생 2021. 4. 20. 12:32

● 변경 정보 확인

개발을 진행하다 보면 두개의 값을 비교해야 하여 서로 다른 데이터를 확인해야 하는 경우가 있다.

예를 들면 변경 이력을 저장하고 히스토리로 활용하고자 할 경우이다.

zjsonpatch.JsonDiff 와 guava.MapDifference를 확인 하고 어떻게 데이터를 비교하여 보여주는지 알아보자

 

● git 주소

bitbucket.org/technology-team/object-differing/src/master/

● 공통설정

데이터가 다른 오브젝트를 만들기 위해 dto를 생성합니다.

- Member.class

import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.Singular;

@Setter
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Member {
    private int id;

    private String email;

    private String password;

    private String address;

    private String phone;

    @Singular
    private List<Role> roles;

    private Member parent;
}

- Role.class

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.NoArgsConstructor;
import lombok.Setter;
import lombok.Getter;

@Setter
@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Role {
    private int id;

    private String role;
}

● JsonDiff 

1. dependency 추가

 

implementation 'com.flipkart.zjsonpatch:zjsonpatch:0.4.11'

2. JsonDiff 확인 로직

import com.example.objectdiffering.dto.Member;
import com.example.objectdiffering.dto.Role;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.flipkart.zjsonpatch.JsonDiff;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class JsonDiffExample {

    public static void main(String[] args) {
        Member original = Member.builder()
                                .id(1)
                                .email("test@email.com")
                                .password("password")
                                .role(Role.builder()
                                          .id(1)
                                          .role("ROLE_ADMIN")
                                          .build())
                                .role(Role.builder()
                                          .id(3)
                                          .role("ROLE_USER")
                                          .build())
                                .parent(Member.builder()
                                              .id(2)
                                              .email("test@email.com")
                                              .password("password")
                                              .build())
                                .build();

        Member change = Member.builder()
                              .id(1)
                              .email("changeTest@email.com")
                              .role(Role.builder()
                                        .id(1)
                                        .role("ROLE_ADMIN")
                                        .build())
                              .parent(Member.builder()
                                            .id(3)
                                            .email("test@email.com")
                                            .password("password")
                                            .build())
                              .build();

        ObjectMapper mapper = new ObjectMapper();
        JsonNode diffNode = JsonDiff.asJson(mapper.valueToTree(original), mapper.valueToTree(change));
        System.out.println(diffNode.toPrettyString());
    }
}

3. 결과값

[ {
  "op" : "replace",
  "path" : "/email",
  "value" : "changeTest@email.com"
}, {
  "op" : "replace",
  "path" : "/password",
  "value" : null
}, {
  "op" : "remove",
  "path" : "/roles/1"
}, {
  "op" : "replace",
  "path" : "/parent/id",
  "value" : 3
} ]

MapDifference

1. dependency 추가

implementation 'com.google.guava:guava:30.1.1-jre'

2. MapDifference 로직

import java.util.Map;

import com.example.objectdiffering.dto.Member;
import com.example.objectdiffering.dto.Role;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.MapDifference;
import com.google.common.collect.MapDifference.ValueDifference;
import com.google.common.collect.Maps;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class MapDiffExample {

    public static void main(String[] args) {
        Member original = Member.builder()
                                .id(1)
                                .email("test@email.com")
                                .password("password")
                                .role(Role.builder()
                                          .id(1)
                                          .role("ROLE_ADMIN")
                                          .build())
                                .role(Role.builder()
                                          .id(3)
                                          .role("ROLE_USER")
                                          .build())
                                .parent(Member.builder()
                                              .id(2)
                                              .email("test@email.com")
                                              .password("password")
                                              .build())
                                .build();

        Member change = Member.builder()
                              .id(1)
                              .email("changeTest@email.com")
                              .phone("010-1234-5678")
                              .role(Role.builder()
                                        .id(1)
                                        .role("ROLE_ADMIN")
                                        .build())
                              .parent(Member.builder()
                                            .id(3)
                                            .email("test@email.com")
                                            .password("password")
                                            .build())
                              .build();

        ObjectMapper mapper = new ObjectMapper();
        Map originalMap = mapper.convertValue(original, Map.class);
        Map changeMap = mapper.convertValue(change, Map.class);
        MapDifference<String, Object> difference = Maps.difference(originalMap, changeMap);
        Map<String, ValueDifference<Object>> differenceMap = difference.entriesDiffering();

        difference.entriesDiffering().forEach((key, value) -> System.out.println(key + ": " + value));
    }
}

3. 결과값

email: (test@email.com, changeTest@email.com)
password: (password, null)
phone: (null, 010-1234-5678)
roles: ([{id=1, role=ROLE_ADMIN}, {id=3, role=ROLE_USER}], [{id=1, role=ROLE_ADMIN}])
parent: ({id=2, email=test@email.com, password=password, address=null, phone=null, roles=[], parent=null}, {id=3, email=test@email.com, password=password, address=null, phone=null, roles=[], parent=null})

JsonDiff vs MapDifference 비교

  JsonDiff MapDifference
비교 필드 하위 필드까지 비교하여 추가/수정/삭제된 내용을 명확히 체크 object 단위 기준으로 변경된 정보가 있는지 체크
수정/삭제등의 여부를 값을 보고 사용자가 판단
결과 값 수정된 결과만 보여준다 이전값과 수정된 결과값을 보여준다.

JsonDiff가 이전값의 필드도 같이 보여줬으면 하는 아쉬움이 있다. 이부분은 JsonDiff 라이브러리를 수정하면 가능해 보인다. 상황에 맞게 라이브러리를 사용하면 될것으로 보인다.

 

 참고 사이트

cassiomolin.com/2018/07/23/comparing-json-documents-in-java/