Spring Boot LocalDate field serialization and deserialization

51.4k Views Asked by At

In Spring Boot 1.2.3.RELEASE with fasterxml what is the correct way of serializing and de-serializing a LocalDate field to ISO date formatted string?

I've tried:

  • spring.jackson.serialization.write-dates-as-timestamps:false in application.properties file,

  • including jackson-datatype-jsr310 in project and then using

    • @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd") annotation

    • and @DateTimeFormat(iso=ISO.DATE) annotation,

  • adding Jsr310DateTimeFormatAnnotationFormatterFactory as formatter with:

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addFormatterForFieldAnnotation(new Jsr310DateTimeFormatAnnotationFormatterFactory());
    }
    

None of the above helped.

6

There are 6 best solutions below

5
On BEST ANSWER
compile ("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")

in build.gradle and then following annotations helped:

@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
private LocalDate birthday;

Update: if you are using Spring Boot 2.*, the dependency is already included via one of the "starters".

0
On

In my Spring Boot 2 applications

  • @JsonFormat annotation is used in REST controllers when (de)serializing JSON data.
  • @DateTimeFormat annotation is used in other controllers ModelAttributes when (de)serializing String data.

You can specify both on the same field (useful if you share DTO between JSON and Thymeleaf templates):

@JsonFormat(pattern = "dd/MM/yyyy") 
@DateTimeFormat(pattern = "dd/MM/yyyy")
private LocalDate birthdate;

Gradle dependency:

implementation group: 'org.springframework.boot', name: 'spring-boot-starter-json'

I hope this is all configuration you need for custom Date/Time formatting in Spring Boot 2.x apps.


For Spring Boot 1.x apps, specify additional annotations and dependency:

@JsonFormat(pattern = "dd/MM/yyyy")
@DateTimeFormat(pattern = "dd/MM/yyyy")
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
private LocalDate birthDate;

// or
@JsonFormat(pattern = "dd/MM/yyyy HH:mm")
@DateTimeFormat(pattern = "dd/MM/yyyy HH:mm")
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime birthDateTime;
implementation group: 'com.fasterxml.jackson.datatype', name: 'jackson-datatype-jsr310', version: '2.9.8'

Be aware that your API will throw "JSON parse error" if somebody sends a date in a wrong format. Mapping example:

@JsonFormat(pattern = "yyyy-MM-dd")
private LocalDate releaseDate;

Exception example:

HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type java.time.LocalDate from String "2002": Failed to deserialize java.time.LocalDate: (java.time.format.DateTimeParseException) Text '2002' could not be parsed at index 4; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type java.time.LocalDate from String "2002": Failed to deserialize java.time.LocalDate: (java.time.format.DateTimeParseException) Text '2002' could not be parsed at index 4

0
On

Instead of specifying the serializer/deserializer for all your LocalDate attributes, you might be interested in registering it globally. You need to override the configuration of the default ObjectMapper used by Spring for serialization/deserialization. Here is an example:

@Configuration
public class ObjectMapperConfiguration {

    @Bean
    @Primary
    public ObjectMapper objectMapper() {
        ObjectMapper mapper = new ObjectMapper();

        // Registro global do serializer/deserializer para datas sem horário
        SimpleModule simpleModule = new SimpleModule();
        simpleModule.addSerializer(LocalDate.class, new LocalDateSerializer());
        simpleModule.addDeserializer(LocalDate.class, new LocalDateDeserializer());

        mapper.registerModule(simpleModule);
        return mapper;
    }
}

This way, Jackson will always use the specified serializer/deserializer for the data type defined, without the need to annotate your classes attributes.

Info: you must add the jackson-databind dependency to your pom.xml to have the custom serializer/deserializer code feature:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.3</version>
</dependency>
0
On

Actually, it works if you just specify the dependency in the pom.xml.

With this, all my LocalDate fields automatically use the ISO format, no need to annotate them:

<!-- This is enough for LocalDate to be deserialized using ISO format -->
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

Tested on Spring Boot 1.5.7.

1
On
 @Bean
public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
    return builder -> {
        DateTimeFormatter localDateFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
        builder.serializerByType(LocalDate.class, new LocalDateSerializer(localDateFormatter));
        builder.deserializerByType(LocalDate.class, new LocalDateDeserializer(localDateFormatter));

        DateTimeFormatter localDateTimeFormatter = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss");
        builder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(localDateTimeFormatter));
        builder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(localDateTimeFormatter));
    };
}
1
On

If you want to use a custom Java Date formatter, add the @JsonFormat annotation.

@JsonFormat(pattern = "dd/MM/yyyy")
@JsonDeserialize(using = LocalDateDeserializer.class)
@JsonSerialize(using = LocalDateSerializer.class)
private LocalDate birthdate;*