Darren's Devlog

[ObjectMapper] CustomDeserializer 만들기 본문

Spring

[ObjectMapper] CustomDeserializer 만들기

Darren Gwon 2023. 2. 4. 00:10
반응형

ObjectMapper은 데이터 포맷 중 하나인 JSON을 간편하게 POJO로 다룰 수 있는 기능들을 제공합니다.

또한 매우 Customizable하기 때문에 고도한 Object 처리를 가능하게 해줍니다.

 

오늘은 JSON → POJO로 변환하는 과정(Deserialize, 역직렬화)에서 발생한

InvalidFormatException을 해결할 수 있는 CustomDeserializer클래스를 만들어보겠습니다.


1. 시나리오

Book JSON

[
  {
    "name":"자바의정석",
    "reviewCount":"735"
  },
  {
    "name":"토비의스프링",
    "reviewCount":"2,413"
  }
]

 

BookDto

@Getter
@Setter
public class BookDto {
    private String name;
    private int reviewCount;
}

 

Service

@Service
@RequiredArgsConstructor
public class BookService {
    private final ObjectMapper objectMapper;
    
    public List<BookDto> getBooks(JsonNode responseNode) throws JsonProcessingException {
        List<BookDto> bookList = objectMapper.readValue(responseNode.toString(), new TypeReference<List<BookDto>>() {});
        return bookList;
    }
}

Book JSON이 담긴 responseNode를 List<BookDto>로 변환해주는 코드입니다.

"자바의정석" 데이터의 경우 BookDto로 변환하는데 문제가 없지만,

"토비의스프링" 도서 데이터처럼 콤마(comma)를 포함하는 경우엔 BookDto로 변환이 불가능합니다.

 

형변환 에러 로그

com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `java.lang.Integer` from String "2,413": not a valid `java.lang.Integer` value

 

 

reviewCount는 엄연한 숫자 데이터이지만, 위처럼 변환이 불가능한 경우에는

CustomDeserializer 클래스를 정의하여 등록해주면 ObjectMapper를 보다 유용하게 사용 가능합니다.


2. 구현 코드

CustomIntegerDeserializer

public class CustomIntegerDeserializer extends JsonDeserializer<Integer> {
    @Override
    public Integer deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException {
        String stringNumber = p.getValueAsString();
        if(Objects.isNull(stringNumber) || "".equals(stringNumber)) return null;

        return Integer.parseInt(stringNumber.replace(",", ""));
    }
}

JsonDeserializer를 상속받아 deserialize메서드를 오버라이딩하는 커스텀 클래스를 만들어줍니다.

콤마를 stringNumber.replace(",", "")로 변환 후, Integer.parseInt()를 통해 int형으로 반환합니다.

 

 

BookDto

@Getter
@Setter
public class BookDto {
    private String name;
    private int reviewCount;
}

reviewCount의 타입을 기본형 int에서 Wrapper클래스인 Integer로 변경해줍니다.

기본형 int타입의 경우 의도한대로 변환이 불가능합니다.

 

ObjectMapper

@Configuration
public class SpringConfig {
    @Bean
    public ObjectMapper objectMapper() {
        return JsonMapper.builder()
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .addModule(new SimpleModule().addDeserializer(Integer.class, new CustomIntegerDeserializer()))
                .build();
    }
}

위 코드처럼 CustomIntegerDeserializer 클래스를 module로 추가해주면

선언된 타입이 Integer일 때, 역직렬화 과정에서 CustomIntegerDeserializer 클래스에 의해 변환이 이루어집니다.

반응형
Comments