How do I deserialize timestamps that are in seconds with Jackson?

40,667

Solution 1

I wrote a custom deserializer to handle timestamps in seconds (Groovy syntax).

class UnixTimestampDeserializer extends JsonDeserializer<DateTime> {
    Logger logger = LoggerFactory.getLogger(UnixTimestampDeserializer.class)

    @Override
    DateTime deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
        String timestamp = jp.getText().trim()

        try {
            return new DateTime(Long.valueOf(timestamp + '000'))
        } catch (NumberFormatException e) {
            logger.warn('Unable to deserialize timestamp: ' + timestamp, e)
            return null
        }
    }
}

And then I annotated my POGO to use that for the timestamp:

class TimestampThing {
    @JsonDeserialize(using = UnixTimestampDeserializer.class)
    DateTime timestamp

    @JsonCreator
    public TimestampThing(@JsonProperty('timestamp') DateTime timestamp) {
        this.timestamp = timestamp
    }
}

Solution 2

@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="s")
public Date timestamp;

edit: vivek-kothari suggestion

@JsonFormat(shape=JsonFormat.Shape.NUMBER, pattern="s")
public Timestamp timestamp;

Solution 3

A very similar approach to that of @DrewStephens's which uses the Java SE TimeUnit API (introduced in JDK1.5) instead of plain String concatenation and is thus (arguably) a little bit cleaner and more expressive:

public class UnixTimestampDeserializer extends JsonDeserializer<Date> {

    @Override
    public Date deserialize(JsonParser parser, DeserializationContext context) 
            throws IOException, JsonProcessingException {
        String unixTimestamp = parser.getText().trim();
        return new Date(TimeUnit.SECONDS.toMillis(Long.valueOf(unixTimestamp)));
    }
}

Specifying your custom deserializer (UnixTimestampDeserializer) on the affected Date field(s):

@JsonDeserialize(using = UnixTimestampDeserializer.class)
private Date updatedAt;

Solution 4

I had this exact issue, except my ZonedDateTime objects got turned to unix-timestamps (seconds) and I needed them in milliseconds (to initialize JS Date objects on the browser).

Implementing a custom serializer/deserializer looked like too much work for something that should be pretty straightforward, so I looked elsewhere and found that I can just configure the object mapper for the desired result.

Because my application already overrides the default ObjectMapper provided by Jersey, I just had to disable SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS.

Here's my code

@Provider
public class RPObjectMapperProvider implements ContextResolver<ObjectMapper> {

    final ObjectMapper defaultObjectMapper;

    public RPObjectMapperProvider() {
        defaultObjectMapper = new ObjectMapper();

        // this turned ZonedDateTime objects to proper seconds timestamps 
        defaultObjectMapper.enable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

        // disable to serialize dates for millis timestamps
        defaultObjectMapper.disable(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS);

        // using Java8 date time classes
        defaultObjectMapper.registerModule(new JavaTimeModule());
    }

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

And that's it

Share:
40,667
Drew Stephens
Author by

Drew Stephens

My website

Updated on May 23, 2020

Comments

  • Drew Stephens
    Drew Stephens almost 4 years

    I've got some JSON that has timestamps in seconds (i.e. a Unix timestamp):

    {"foo":"bar","timestamp":1386280997}
    

    Asking Jackson to deserialize this into an object with a DateTime field for the timestamp results in 1970-01-17T01:11:25.983Z, a time shortly after the epoch because Jackson is assuming it to be in milliseconds. Aside from ripping apart the JSON and adding some zeros, how might I get Jackson to understand the seconds timestamp?

  • sherpya
    sherpya over 9 years
    why downvote? because it is easier than other solutions?
  • Renaud Cerrato
    Renaud Cerrato about 9 years
    probably because its timestamps are represented as integer and not as string in json.
  • sherpya
    sherpya about 9 years
    jackson does required type conversion
  • Renaud Cerrato
    Renaud Cerrato about 9 years
    {"timestamp" : "42"} is not the same as {"timestamp" : 42}, and I'm assuming that your JsonFormat will expect double quotes around.
  • sherpya
    sherpya about 9 years
    looks like jackson does automatic type conversion using the field type and not the intermediate data type used by formatter
  • Ralph
    Ralph almost 9 years
    This does not work, because it {{s}} mean not the seconds since 1970, it mean the seconds of the "given" date (0..59)
  • tuscland
    tuscland over 8 years
    Hello, just out of curiousity what dialect of Java is it? Thanks!
  • thomi
    thomi over 8 years
    Works like a charm in ordinary boring java, too. Just use the Annotation on whatever DateTime field you need. Did not try using Java 8 Date, but should be no problem there either. Thanks
  • Jan Papenbrock
    Jan Papenbrock over 7 years
    I used NUMBER_INT instead of NUMBER to get rid of decimal places - so in full: @JsonFormat(shape=JsonFormat.Shape.NUMBER_INT, pattern="s")
  • acheron55
    acheron55 over 7 years
    This only works with java.sql.Timestamp, there is no way to make it work with java.util.Date if your json contains number
  • Pierrick
    Pierrick about 7 years
    If you can use java8 you're maybe interested in using java.time.Instant class and to use regular jackson datatypes. This way you just need @JsonProperty("timestamp") private Instant timestamp
  • lilalinux
    lilalinux about 6 years
    @Pierrick Please turn this into an answer, so I can upvote ;-)