Jackson deserialize ISO8601 formatted date-time into Java8 Instant
JavaJsonDatetimeJacksonJava 8Java Problem Overview
I'm trying to deserialize an ISO8601 formatted date into Java8 java.time.Instant
using Jackson. I registered JavaTimeModule with the ObjectMapper, and turned off the WRITE_DATES_AS_TIMESTAMPS
setting.
However, if one tries to deserialize 2016-03-28T19:00:00.000+01:00
it will not work, because it seems that JavaTimeModule will only deserialize date-times formatted with UTC timezone offset (e.g. 2016-03-28T18:00:00.000Z
). I then tried using @JsonFormat
annotation like this:
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = "UTC")
And like this:
@JsonFormat(shape=JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone = JsonFormat.DEFAULT_TIMEZONE)
However, neither of these work and I get an exception:
com.fasterxml.jackson.databind.JsonMappingException: Unsupported field: YearOfEra (through reference chain: org.example.Article["date"])
Which implies that timezone parameter is ignored and date time formatter doesn't know how to format an Instant without a timezone.
Is there a way to deserialize a ISO8601 string that's not in UTC time zone offset to Java 8 java.time.Instant
using Jackson and JavaTimeModule without writing a custom deserializer?
Java Solutions
Solution 1 - Java
You need to set the explicit time zone via XXX
in your modell class:
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX")
(see: https://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html)
Solution 2 - Java
If you want to serialize Date
objects into ISO-8601, you don't need to specify a pattern at all - ISO-8601 is the default pattern. It is kind of mentioned in the JsonFormat
Java doc:
> Common uses include choosing between alternate representations -- for example, whether Date is to be serialized as number (Java timestamp) or String (such as ISO-8601 compatible time value) -- as well as configuring exact details with pattern() property.
[emphasasis mine] you should understand from the above text that specifying shape = STRING
would mean an ISO-8601 format but you can choose something else using the pattern
property.
In my experience, this always turns out a UTC date format (with the time zone rendered as +0000
), which could be the default time zone in my VM (even though my operating system clock is not set to UTC).
Solution 3 - Java
In Jackson 2.9.8 (current one as I'm writing this) it's better to use Instant instead of Date.
You have to add a dependency:
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr310</artifactId>
<version>2.9.8</version>
</dependency>
Also, register the module and configure SerializationFeature.WRITE_DATES_AS_TIMESTAMPS to false.
new ObjectMapper()
.findAndRegisterModules()
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
More information about Jackson for Java8 here: https://github.com/FasterXML/jackson-modules-java8
Solution 4 - Java
The format "Z" does not work with "+01:00" as this is a different pattern. JsonFormat is using SimpleDateFormat patterns. "Z" in upper case only represents strict RFC 822. You have to use syntax like: "+0100", without colon.
Solution 5 - Java
Jackson can be configured globally (without annotations) to accept timestamps with or without colon:
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(new StdDateFormat().withColonInTimeZone(true));
The default Jackson timezone format was changed since version 2.11 from '+0000' to '+00:00'. Both formats are valid according to ISO-8601.