Krešimir Nesek
Krešimir Nesek

Reputation: 5502

Jackson deserialize ISO8601 formatted date-time into Java8 Instant

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?

Upvotes: 76

Views: 137333

Answers (6)

Rama Sharma
Rama Sharma

Reputation: 116

I was facing this issue while sending Instant data type variable on a RMQ queue. It was giving errors in different scenarios .

1st error that i encountered was

java.base/java.lang.Thread.run(Thread.java:829)\nCaused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Java 8 date/time type java.time.Instant not supported by default:

Solution was too add dependency of

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

Then second issue I started facing was that it was not able to serialise the date format . I tried to load JAVA TIME Modules with object mapper but that didn't help me. What worked for me is as below.

@JsonSerialize(using = InstantSerializer.class)
@JsonDeserialize(using = CustomInstantDeserializer.class)
Instant updateTime;

CustomeDeserialser i had to write because it was giving error that InvalidDefinitionException: Class com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer has no default (no arg) constructor\n

public class CustomInstantDeserializer extends 
 InstantDeserializer<Instant> { 
      public CustomInstantDeserializer() {
           super(InstantDeserializer.INSTANT,
            new DateTimeFormatterBuilder().
     parseCaseInsensitive()
   .append(DateTimeFormatter.ISO_LOCAL_DATE_TIME)
                    .optionalStart().appendOffset("+HH:MM", 
  "+00:00").optionalEnd().optionalStart()
                    .appendOffset("+HHMM", 
    "+0000").optionalEnd().optionalStart().appendOffset("+HH", "Z")
                    .optionalEnd().toFormatter());
      }

    }

Upvotes: 1

Dimitar II
Dimitar II

Reputation: 2519

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.

Upvotes: 9

Meiko Rachimow
Meiko Rachimow

Reputation: 4724

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)

Upvotes: 82

zoomout
zoomout

Reputation: 449

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

Upvotes: 23

Guss
Guss

Reputation: 32335

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

Upvotes: 19

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.

See: ISO 8601:2004, SimpleDateFormat patterns

Upvotes: 4

Related Questions