I have the following problem I hope someone can give me a hand:
Context: 3 Rest endpoints
register
)findKid
) listDashboardInfo
)Requirement: Use the same date format yyyyMMdd
for LocalDates in the whole application
Problem: Using @DateTimeFormat(pattern = DateUtils.SHORT_DATE_PATTERN)
works for register
and listDashboardInfo
but not for findKid
These are the relevant parts of the code:
BODY
{
"sailDate": "20191201"
}
@PostMapping(KID_PATH)
@ResponseStatus(HttpStatus.CREATED)
public KidDTO register(@RequestBody @Valid KidDTO kid) {
return kidService.saveKid(kid);
}
GET /kid/0001::20190901
RESPONSE
{
"sailDate": "2019-09-01"
}
@GetMapping(KID_FIND_PATH)
public CompletableFuture<KidDTO> findKid(@PathVariable String id) {
return kidService.findKid(id);
}
GET /kid?shipCode=AL&sailDate=20190901
@GetMapping(KID_LIST_PATH)
public CompletableFuture<Slice<DashboardDTO>> listDashboardInfo(@Valid DashboardFilter filter, Pageable pageable) {
return kidService.listKidsWithStatistics(filter, pageable);
}
@Getter
@Setter
public class DashboardFilter {
@NotNull
@DateTimeFormat(pattern = DateUtils.SHORT_DATE_PATTERN)
private LocalDate sailDate;
}
@Data
public class KidDTO {
@NotNull
@DateTimeFormat(pattern = DateUtils.SHORT_DATE_PATTERN)
private LocalDate sailDate;
}
Tests I did:
@JsonFormat(pattern = DateUtils.SHORT_DATE_PATTERN)
the listDashboardInfo
doesn't recognize the format and generates errorFrom stackoverflow I also found Spring doesn't use Jackson to deserialize query params so:
- I created a @ControllerAdvice
with @InitBinder
but the method setAsText
is never called:
@ControllerAdvice
public class GlobalDateBinder {
@InitBinder
public void binder(WebDataBinder binder) {
binder.registerCustomEditor(LocalDate.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) throws IllegalArgumentException {
LocalDate.parse(text, DateUtils.SHORT_DATE_FORMATTER);
}
});
}
}
@Bean public Formatter<LocalDate> localDateFormatter()
but nothing change: @Bean
public FormattingConversionService conversionService() {
DefaultFormattingConversionService conversionService =
new DefaultFormattingConversionService(false);
DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
registrar.setDateFormatter(DateUtils.SHORT_DATE_FORMATTER);
registrar.registerFormatters(conversionService);
return conversionService;
}
@Bean
public Formatter<LocalDate> localDateFormatter() {
return new Formatter<LocalDate>() {
@Override
public LocalDate parse(String text, Locale locale) {
return LocalDate.parse(text, DateUtils.SHORT_DATE_FORMATTER);
}
@Override
public String print(LocalDate object, Locale locale) {
return DateUtils.SHORT_DATE_FORMATTER.format(object);
}
};
}
Any one has an idea of what is happening?
how to make the response of findKid
be formatted?
How to configure the whole application with the same date format to works in serialization and parsing/deserializing processes?
UPDATE:
I found here https://stackoverflow.com/questions/30871255/spring-boot-localdate-field-serialization-and-deserialization
that I can use @JsonFormat for rest controllers (serialize and deserialize) and @DateTimeFormat for ModelView controllers but using both, at the same time, fixed my error so I don't understand why is that behavior if I only have rest controllers. Looks like in my case @DateTimeFormat deserialize and @JsonFormat serialize, is that the expected behavior? Is there any misconfiguration?
you can add this bean to you configuration:
@Bean
public ObjectMapper objectMapper() {
DateTimeFormatter dateFormatter; // create your date formatter
DateTimeFormatter dateTimeFormatter; // create your date and time formatter
ObjectMapper mapper = new ObjectMapper();
SimpleModule localDateModule = new SimpleModule();
localDateModule.addDeserializer(LocalDate.class,
new LocalDateDeserializer(formatter));
localDateModule.addSerializer(LocalDate.class,
new LocalDateSerializer(formatter));
localDateModule.addDeserializer(LocalDateTime.class,
new LocalDateTimeDeserializer(dateTimeFormatter));
localDateModule.addSerializer(LocalDateTime.class,
new LocalDateTimeSerializer(dateTimeFormatter));
mapper.registerModules(localDateModule);
return mapper;
}