How do I configure Jackson Serialization on LocalDateTime and LocalDate for Java?

12,774

Solution 1

The JavaTimeModule will do the hard work for you. It provides a set of serializers and deserializers for for the java.time types. If the SerializationFeature.WRITE_DATES_AS_TIMESTAMPS is disabled, java.time types will be serialized in standard ISO-8601 string representations.


By default, Spring will provide you with an instance of ObjectMappper created by the Jackson2ObjectMapperBuilder. This instance is auto-configured for you. See the class documentation for details.

If you want to replace the default ObjectMapper completely, either define a @Bean of that type and mark it as @Primary:

@Bean
@Primary
public ObjectMapper objectMapper() {    
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JavaTimeModule());
    mapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    return mapper;
}

Or, if you prefer the builder-based approach, define a Jackson2ObjectMapperBuilder @Bean.

@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.modules(new JavaTimeModule());
    builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
    return builder;
}

Note that, in either case, doing so disables all auto-configuration of the ObjectMapper. See the documentation for details.

Solution 2

I finally got this working with the help of Cassio and some other digging on the web. Looking back I think the issue was that I was defining the custom formats on the JavaTimeModule, but I needed to define them on the mapper instead.

@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
    return new Jackson2ObjectMapperBuilder()
            .createXmlMapper(false)
            .indentOutput(true)
            .featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
            .serializers(
                    new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")),
                    new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
            .deserializers(
                    new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")),
                    new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")))
            .modules(
                    new JavaTimeModule(),
                    new ParameterNamesModule(),
                    new Jdk8Module(),
                    new JtsModule());
}

https://github.com/spring-projects/spring-boot/issues/4217

http://www.baeldung.com/jackson-serialize-dates

Share:
12,774
wheelerswebservices
Author by

wheelerswebservices

Technologist with a background in programming and a lot of knowledge in Cloud Computing with platforms like AWS, Azure, and GCP. I'm incredibly fascinated by cutting edge technologies like AI, Blockchain, ML, and Quantum Computing.

Updated on June 05, 2022

Comments

  • wheelerswebservices
    wheelerswebservices almost 2 years

    I know that there are many posts on Stackoverflow regarding this topic and I'm sure I've read just about all of them, but I am still struggling to get this working and would appreciate any guidance.

    This is the Spring Parent and Jackson Dependencies I am using:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
    </parent>
    
    <!-- Jackson -->
    <dependency>
        <groupId>com.fasterxml.jackson.module</groupId>
        <artifactId>jackson-module-parameter-names</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jdk8</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
    </dependency>
    

    Within my Application Configuration I am trying to use:

    @Autowired
    void configureJackson(ObjectMapper jackson2ObjectMapper) {
    
        JavaTimeModule timeModule = new JavaTimeModule();
    
        timeModule.addDeserializer(LocalDate.class,
                new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
    
        timeModule.addDeserializer(LocalDateTime.class,
                new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    
        timeModule.addSerializer(LocalDate.class,
                new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
    
        timeModule.addSerializer(LocalDateTime.class,
                new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    
        jackson2ObjectMapper
                .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
                .setSerializationInclusion(JsonInclude.Include.NON_NULL)
                .registerModule(timeModule)
                .registerModule(new ParameterNamesModule())
                .registerModule(new Jdk8Module())
                .registerModule(new JtsModule());
    }
    

    This is the model I am testing with. (I am trying to achive the same in 5-6 models).

    public class DateRange implements Serializable {
    
         private static final long serialVersionUID = 6412487507434252330L;
    
         private LocalDateTime startTime;
         private LocalDateTime endTime;
    
         private LocalDate startDate;
         private LocalDate endDate;
    
         // Getters/Setters
    

    I am hoping that I could find an approach that would apply globally without the need for me to annotate each field individually. I was using java.util.Date before, but ran into some issues with other functionality there.

    Trying to use these newer (better) date models instead of that old damaged one is causing me a lot of headache over a simple thing.


    Update

    I changed my configuration to one of the suggested below and came really close to solving this issue.

    @Bean
    @Primary
    public ObjectMapper objectMapper() {
    
        JavaTimeModule timeModule = new JavaTimeModule();
    
        timeModule.addDeserializer(LocalDate.class,
                new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
    
        timeModule.addDeserializer(LocalDateTime.class,
                new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    
        timeModule.addSerializer(LocalDate.class,
                new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
    
        timeModule.addSerializer(LocalDateTime.class,
                new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
    
        return new ObjectMapper()
                //.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .setSerializationInclusion(JsonInclude.Include.NON_NULL)
                .registerModule(timeModule)
                .registerModule(new ParameterNamesModule())
                .registerModule(new Jdk8Module())
                .registerModule(new JtsModule());
    }
    

    Now the only issue I have is that LocalDateTime is printing timestamps like: "2018-07-26T07:57:12.938" when I need them like: 2018-07-26 07:57:12.

    These fields are already in-use today and I need to make this change seamless in a way that doesn't require my API consumers to make any adjustments.

    Cassio mentioned that disabling SerializationFeature.WRITE_DATES_AS_TIMESTAMPS will print timestamps in this ISO format, but it's not what I need. I tried to comment out the field in the hopes that it would pickup the custom DateTimeFormatter I am providing, however it has not changed my output.

    • wheelerswebservices
      wheelerswebservices almost 6 years
      This DateRange object above for one, but anywhere in my code that uses these classes LocalDateTime and LocalDate I want to be handled globally; if possible.
  • wheelerswebservices
    wheelerswebservices almost 6 years
    Thanks for your answer. This helped me get a lot closer than I was, however, I can't seem to change the format that LocalDateTime is printed in; even when providing a custom DateTimeFormatter implementation. I updated my question above to show the code I tried and the updated progress towards solving this issue.
  • cassiomolin
    cassiomolin almost 6 years
    @jDub9 I've just created a standalone application, configured ObjectMapper with the serializers show in your question, serialized your model as JSON as the dates were serialized in the correct format. Also, uncomment .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).
  • wheelerswebservices
    wheelerswebservices almost 6 years
    Okay so I uncommented that line, ran a mvn clean install to ensure my classpath wasn't using any old settings, and I am still seeing timestamps serialized as: 2018-07-26T07:57:12.938.
  • Philippe Simo
    Philippe Simo over 2 years
    Wondering how to get the ObjectMapper from this bean ? Inject it in a class: @Autowired Jackson2ObjectMapperBuilder objectMapperBuilder; then ObjectMapper mapper = objectMapperBuilder.build();