[JSON parse error]LocalDateTime Jackson 에러

Sony·2021년 1월 19일
0

컨트롤러 테스트코드를 작성하던 중 LocalDateTime을 DTO에서 파싱할 때 에러가 발생했다. 찾아보니 자바의LocalDateTime을 사용해서 JSON으로 통신할 때 이런 직렬화 에러가 많이 생기는 것 같다.

에러 메시지

JSON parse error: Expected array or string.; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Expected array or string.
 at [Source: (PushbackInputStream); line: 1, column: 365] (through reference chain: com.menu.api.pilot.api.menu.dto.request.MenuCreateRequest["menuDisplaySchedule"]->com.menu.api.pilot.api.menu.dto.request.MenuDisplayScheduleRequest["displayStartDate"])]

해결 방법1

  • 테스트코드에서만 사용할 Bean을 만들어서 해결
  • Bean에서 사용하는 ObjectMapper를 재정의한다.
package com.menu.api.pilot.config;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import java.io.IOException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;

@Configuration
public class JacksonConfiguration {

    public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");

    @Bean
    @Primary
    public ObjectMapper serializingObjectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        JavaTimeModule javaTimeModule = new JavaTimeModule();
        javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer());
        javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());
        objectMapper.registerModule(javaTimeModule);
        return objectMapper;
    }

    public class LocalDateSerializer extends JsonSerializer<LocalDate> {

        @Override
        public void serialize(LocalDate value, JsonGenerator gen, SerializerProvider serializers) throws IOException, IOException {
            gen.writeString(value.format(FORMATTER));
        }
    }

    public class LocalDateDeserializer extends JsonDeserializer<LocalDate> {

        @Override
        public LocalDate deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
            return LocalDate.parse(p.getValueAsString(), FORMATTER);
        }
    }
}

LocalDateTime을 사용하는 곳에서 Autowired 해준다.

@ExtendWith(RestDocumentationExtension.class)
@AutoConfigureMockMvc
public abstract class MvcTest {

    @Autowired
    protected ObjectMapper OBJECT_MAPPER;

    @Autowired
    protected MockMvc mockMvc;

...
}

해결 방법2

  • 테스트 코드에서 @BeforeSetup에 ObjectMapper를 다음과 같이 설정
protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

@Autowired
protected MockMvc mockMvc;

@BeforeEach
void setUp(){
    JavaTimeModule javaTimeModule = new JavaTimeModule();
    javaTimeModule.addSerializer(LocalDate.class, new LocalDateSerializer());
    javaTimeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());
    OBJECT_MAPPER.registerModule(javaTimeModule);
    JacksonTester.initFields(this, OBJECT_MAPPER);
}

References

profile
개발하며 배운점과 회고를 남기는 공간입니다.

0개의 댓글