Advertisement
Advertisement


serialize/deserialize java 8 java.time with Jackson JSON mapper


Question

How do I use Jackson JSON mapper with Java 8 LocalDateTime?

org.codehaus.jackson.map.JsonMappingException: Can not instantiate value of type [simple type, class java.time.LocalDateTime] from JSON String; no single-String constructor/factory method (through reference chain: MyDTO["field1"]->SubDTO["date"])

2017/05/30
1
230
5/30/2017 7:02:13 AM

Accepted Answer

There's no need to use custom serializers/deserializers here. Use jackson-modules-java8's datetime module:

Datatype module to make Jackson recognize Java 8 Date & Time API data types (JSR-310).

This module adds support for quite a few classes:

  • Duration
  • Instant
  • LocalDateTime
  • LocalDate
  • LocalTime
  • MonthDay
  • OffsetDateTime
  • OffsetTime
  • Period
  • Year
  • YearMonth
  • ZonedDateTime
  • ZoneId
  • ZoneOffset
2017/09/20
283
9/20/2017 2:26:38 PM

Update: Leaving this answer for historical reasons, but I don't recommend it. Please see the accepted answer above.

Tell Jackson to map using your custom [de]serialization classes:

@JsonSerialize(using = LocalDateTimeSerializer.class)
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
private LocalDateTime ignoreUntil;

provide custom classes:

public class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
    @Override
    public void serialize(LocalDateTime arg0, JsonGenerator arg1, SerializerProvider arg2) throws IOException {
        arg1.writeString(arg0.toString());
    }
}

public class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
    @Override
    public LocalDateTime deserialize(JsonParser arg0, DeserializationContext arg1) throws IOException {
        return LocalDateTime.parse(arg0.getText());
    }
}

random fact: if i nest above classes and don't make them static, the error message is weird: org.springframework.web.HttpMediaTypeNotSupportedException: Content type 'application/json;charset=UTF-8' not supported

2019/02/04

If you are using ObjectMapper class of fasterxml, by default ObjectMapper do not understand the LocalDateTime class, so, you need to add another dependency in your gradle/maven :

compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.7.3'

Now you need to register the datatype support offered by this library into you objectmapper object, this can be done by following :

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();

Now, in your jsonString, you can easily put your java.LocalDateTime field as follows :

{
    "user_id": 1,
    "score": 9,
    "date_time": "2016-05-28T17:39:44.937"
}

By doing all this, your Json file to Java object conversion will work fine, you can read the file by following :

objectMapper.readValue(jsonString, new TypeReference<List<User>>() {
            });
2017/12/18

This maven dependency will solve your problem:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.6.5</version>
</dependency>

One thing I've struggled is that for ZonedDateTime timezone being changed to GMT during deserialization. Turned out, that by default jackson replaces it with one from context.. To keep zone one must disable this 'feature'

Jackson2ObjectMapperBuilder.json()
    .featuresToDisable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)
2017/12/18

I had a similar problem while using Spring boot. With Spring boot 1.5.1.RELEASE all I had to do is to add dependency:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
2017/12/18

If you are using Jersey then you need to add the Maven dependency (jackson-datatype-jsr310) as the others suggested and register your object mapper instance like so:

@Provider
public class JacksonObjectMapper implements ContextResolver<ObjectMapper> {

  final ObjectMapper defaultObjectMapper;

  public JacksonObjectMapper() {
    defaultObjectMapper = createDefaultMapper();
  }

  @Override
  public ObjectMapper getContext(Class<?> type) {
    return defaultObjectMapper;
  }

  private static ObjectMapper createDefaultMapper() {
    final ObjectMapper mapper = new ObjectMapper();    
    mapper.registerModule(new JavaTimeModule());
    return mapper;
  }
}

When registering Jackson in your resources, you need to add this mapper like so:

final ResourceConfig rc = new ResourceConfig().packages("<your package>");
rc
  .register(JacksonObjectMapper.class)
  .register(JacksonJaxbJsonProvider.class);
2017/12/18