컨트롤러 테스트코드를 작성하던 중 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"])]
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;
...
}
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);
}