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
aycanadal 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".

1
Radouane ROUFID 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;*
0
Tomo Česnik 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.

0
naXa stands with Ukraine 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

1
chen kqing 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));
    };
}
0
Diego Victor de Jesus 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>