Deserialize millisecond timestamp to java.time.Instant
Asked Answered
P

4

43

I'm attempting to read a JSON file using Jackson and store one of the fields that is stored as a epoch milliseconds as a Java Instant, however deserialization is not behaving as expected.

Here is what I am seeing when trying to read the timestamp:

1503115200000

Jackson is setting the Instant field as +49601-10-28T16:00:00Z.

This appears to be occurring because Jackson's default is to read the timestamp with Instant.ofEpochSecond(Long l) instead of Instant.ofEpochMilli(Long l).

Is there a way to set the Jackson ObjectMapper to use the ofEpochMilli method instead? This is what I currently have for my ObjectMapper:

ObjectMapper om = new ObjectMapper()
            .registerModule(new JavaTimeModule())
            .configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
            .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .setSerializationInclusion(Include.NON_NULL);

Note

If I change the input JSON to ISO date such as 2017-08-19T04:00:00Z or to epoch seconds such as 1503115200 the Instant field is able to set properly.

Unfortunately the JSON input must be epoch milliseconds e.g. 1503115200000.

Pimple answered 18/8, 2017 at 18:22 Comment(1)
I had similar issue, Spring uses Jackson but library with NimbusJwtEncoder uses Gson. Still looking for a solution.Mishap
P
35

Solution was to add .configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false) to the ObjectMapper. Complete ObjectMapper looks like:

ObjectMapper om = new ObjectMapper()
            .registerModule(new JavaTimeModule())
            .configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
            .configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
            .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
            .configure(DeserializationFeature.READ_DATE_TIMESTAMPS_AS_NANOSECONDS, false)
            .setSerializationInclusion(Include.NON_NULL);
Pimple answered 18/8, 2017 at 18:32 Comment(1)
For more info see the documentationInterviewer
C
21

For those who use Spring Boot and experience this issue during rest request deserialization - provide this in the application.properties:

spring.jackson.deserialization.read-date-timestamps-as-nanoseconds=false

@JB Nizet answer is correct, but since Spring Boot has it's own ObjectMapper inside - you need to configure this value in the properties.

Clearway answered 5/10, 2018 at 16:49 Comment(0)
S
7

A more flexible solution could be to write your own convertor class, if you don't want to edit or create an object mapper.

public class LongToInstantConverter extends StdConverter<Long, Instant> {
    public Instant convert(final Long value) {
        return Instant.ofEpochMilli(value);
    }
}

You just need to add the annotation in your class

@JsonDeserialize(converter = LongToInstantConverter.class)
private Instant sentDate;
Stemma answered 31/8, 2021 at 18:43 Comment(2)
For me, this was a better solution. It limits the impact of the change to only the fields that need the conversion.Deauville
Sadly we cannot always change the serialized object. But thanks for the suggestion.Mishap
W
5

From https://github.com/FasterXML/jackson-modules-java8/blob/master/datetime/src/main/java/com/fasterxml/jackson/datatype/jsr310/JavaTimeModule.java:

The more ambiguous integer types are read as fractional seconds without a decimal point if {@code READ_DATE_TIMESTAMPS_AS_NANOSECONDS} is enabled (it is by default), and otherwise they are read as milliseconds.

So you need to disable READ_DATE_TIMESTAMPS_AS_NANOSECONDS.

Wurst answered 18/8, 2017 at 18:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.