Cannot construct instance of java.time.LocalDateTime - Jackson

Daniel Castillo picture Daniel Castillo · Feb 11, 2019 · Viewed 9.7k times · Source

I have two Spring Boot applications which communicate through JMS Messaging and ActiveMQ.

One app sends to the other app an object which contains a LocalDateTime property. This object is serialized to JSON in order to be sent to the other application.

The problem I'm facing is that Jackson is not able to deserialize the LocalDateTime property when it's trying to map the incoming json to my object. The LocalDateTime property has the following format when it arrives to the "listener app":

"lastSeen":{
  "nano":0,
  "year":2019,
  "monthValue":4,
  "dayOfMonth":8,
  "hour":15,
  "minute":6,
  "second":0,
  "month":"APRIL",
  "dayOfWeek":"MONDAY",
  "dayOfYear":98,
  "chronology":{
    "id":"ISO",
    "calendarType":"iso8601"
  }
}

The exception I'm getting is the following:

org.springframework.jms.support.converter.MessageConversionException: Failed to convert JSON message content; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of java.time.LocalDateTime

I was able to fix this issue temporarily by using the following annotations:

@JsonSerialize(as = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class, as = LocalDateTime.class)
private LocalDateTime lastSeen;

but they belong to jackson datatype jsr310 which is now DEPRECATED.

Is there any way/alternative to deserialize this LocalDateTime property without using the above annotations? Or how do I get this to work using the recommended jackson-modules-java8?

Answer

Mr.Q picture Mr.Q · Oct 17, 2019

I remember I did not have this problem with older versions of spring (or maybe I was LUCKY) But this is how I solved it in Spring boot 2.1.7.RELEASE:

First, add Jackson's support modules in order to support Java 8 features (TimeDate API)

    <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>

Then register a default ObjectMapper bean with custom configurations (to support Java 8) in the Spring.

@Bean
@Primary
public ObjectMapper geObjMapper(){
    return new ObjectMapper()
            .registerModule(new ParameterNamesModule())
            .registerModule(new Jdk8Module())
            .registerModule(new JavaTimeModule());
}

NOTE: @Primary is used as a precautionary measure, so if there are other ObjectsMapper beans on the class-path, the spring will pick this one by default.