Backend/Spring+Boot

ObjectMapper json 직렬화/역직렬화 주의사항

findmypiece 2021. 7. 7. 12:38
728x90

1. 직렬화를 위해서는 각 인스턴스 변수에 대한 Getter 메소드가 포함되어 있거나

   각 인스턴스 변수에 @JsonProperty 가 명시되어 있어야 한다.

   이는 택1로 적용하면 된다.

 

2. 역직렬화를 위해서는 기본생성자가 반드시 필요하다.

   각 인스턴스 변수에 대한 Setter 메소드가 필수로 필요하진 않지만 존재한다면 그것을 활용한다.

   

3. 일반적으로 ObjectMapper 는 기본으로 쓰지 않고 아래와 같이 재정의해서

     프로젝트에서 static 메소드로 사용한다.

public class ObjectMapperUtils {
    private static final ObjectMapper mapper = new ObjectMapper();

    static {
        mapper.registerModule(new JavaTimeModule());
        mapper.setSerializationInclusion(Include.NON_EMPTY); 
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.enable(SerializationFeature.INDENT_OUTPUT);
    }

    public static String serialization(Object object) throws JsonProcessingException {
        return mapper.writeValueAsString(object);
    }

    public static <T> T deserialization(String jsonText, Class<T> valueType) throws JsonProcessingException {
        return (T) mapper.readValue(jsonText, valueType);
    }

    public static <T> T typeConvert(Object object, Class<T> convertType){
        return (T) mapper.convertValue(object, convertType);
    }
}

 

mapper.registerModule(new JavaTimeModule());

이 설정이 없으면 java8 LocalDateTime 이 직렬화/역직렬화 되지 않는다.

LocalDateTime 변환을 위해서는 해당 클래스의 변수에 @JsonFormat 도 함께 설정해야 한다.

이 설정이 없으면 @JsonFormat 가 재대로 동작하지 않는다.

참고로 위에서 재정의한 ObjectMapper 가 사용되지 않는 Spring Controller 에서 

LocalDateTime 을 역직렬화 하고 싶다면 아래와 같이 @DateTimeFormat 을 지정해야 한다.

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul") 
@DateTimeFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss")

 

mapper.setSerializationInclusion(Include.NON_EMPTY);

객체를 직렬화할 때 null이나 빈값은 항목에 포함하지 않는다.

즉, 객체에 포함된 변수 중 null 이거나 빈값은 json 변환에 포함되지 않는다.

Default 는 null 이거나 빈값이어도 무조건 json 변환에 포함된다.

클래스에 선언하는 @JsonInclude(JsonInclude.Include.NON_EMPTY) 과 동일한 역할을 한다.

그런데 실무에서는 잘 사용하지 않는다. 대부분 null 이라도 포맷을 그대로 받아서 확인하길 원한다.

 

mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

json 문자열을 역직렬화할 때 클래스 변수에 매핑되지 않는 없는 키값이 있어도 

에러 없이 무시하고 진행한다. Default 는 이 경우 Exception을 발생시킨다.

json 포맷을 엄격하게 지켜야 하는 경우 설정하지 말아야겠지만 

어차피 사용되지 않는 값이기 때문에 로직은 정상 수행되도록 이 설정을 하는 것이 좋다.

클래스에 선언하는 @JsonIgnoreProperties(ignoreUnknown = true) 와 동일한 역할을 한다.

 

mapper.enable(SerializationFeature.INDENT_OUTPUT);

json 문자열을 콘솔에 출력할 때 포맷팅해서 출력한다.

일반적으로 괄호를 포함한 문자열이 한줄에 모두 표기되는데 

적절한 개행과 들여쓰기를 통해 보기좋게 출력해준다.


위에서 한 설정 중 동일한 설정이지만 ObjectMapper 에서 할 수 있는 것도 있고 각 모델 클래스에서 할 수 있는 것도 있는데 기본적으로 양쪽에 모두 해주는 것이 좋다. Spring 같은 프레임워크를 사용하는 환경이라면 해당 클래스가 위에서 재정의한 ObjectMapper 를 통해서만 변환된다는 보장이 없기 때문이다.

 

만약 각 모델 클래스가 아니라 ObjectMapper 에 직접 해야 하는데 내가 커스텀 한 ObjectMapper 가 사용되지 않는 경우에는 어떻게 하나? 예를 들어 Feign의 경우 body를 serialization, deserialization 할 때 기본적으로 Spring에  포함된 ObjectMapper 를 사용한다. 이때는 아래와 같이 ObjectMapper Bean 팩토리 함수를 재정의하면 된다.

@Bean
public ObjectMapper customObjectMapper() {
	return new ObjectMapper()
		.registerModule(new JavaTimeModule())
		.setSerializationInclusion(Include.NON_EMPTY)
		.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
		.enable(SerializationFeature.INDENT_OUTPUT);
}

 

 

728x90

'Backend > Spring+Boot' 카테고리의 다른 글

@Async 설정  (0) 2021.07.13
CircuitBreaker, Resilience4j, RestClient  (0) 2021.07.09
@RequestMapping consumes, produces  (0) 2021.07.07
SpringBoot Redis Cluster Lettuce 설정  (0) 2021.05.04
SpringBoot embedded Redis Cluster  (0) 2021.05.04