Alper Kanat
Alper Kanat

Reputation: 396

Jackson deserialization issue for ZonedDateTime

I've the following field in a class I use during deserialization of a service that I'm consuming.

private ZonedDateTime transactionDateTime;

The service I'm consuming may return a Date or DateTime using the pattern: yyyy-MM-dd'T'HH:mm:ss.SSSZ

Let me give 2 examples of what the service returns:

While first one works well, the latter causes the following exception to be thrown during deserialization:

java.time.format.DateTimeParseException: Text '2015-11-18T00:00:00.000+0200' could not be parsed at index 23

I'm using;

Does this require a custom deserialization class?

Upvotes: 12

Views: 38358

Answers (5)

Jiajun Cui
Jiajun Cui

Reputation: 81

Jackson deserializer will by default bypass the timezone information and use context timezone to override it, which all ISO8601 will ends to UTC.

Using Spring, this feature can be turned off by:

spring.jackson.deserialization.ADJUST_DATES_TO_CONTEXT_TIME_ZONE=false

Upvotes: 8

Michael Belkin
Michael Belkin

Reputation: 41

The following configuration helped me

Specify datetime pattern:

public class Timestamp {

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
    private ZonedDateTime timestamp;

}

Disable converting ZonedDateTime to UTC:

objectMapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);

Also there was another problem, which may be tricky to spot. In my case I was receiving Timestamp from another micro service using RestTemplate. RestTemplate can be configured to use notProjectDefaultObjectMapper, which is not affected by default Spring Jackson configuration approaches (like application properties, or @Configuration classes with Jackson2ObjectMapperBuilderCustomizer bean definition or somehow else). So RestTemplate's objectMapper (if there is one) should be configured too

Upvotes: 1

Vova Rozhkov
Vova Rozhkov

Reputation: 1732

I've used

@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ssXXX")
private ZonedDateTime startDate;

plus jackson-datatype-jsr310 library, obviously.

This solution is described in Jackson deserialize ISO8601 formatted date-time into Java8 Instant

Upvotes: 1

Alper Kanat
Alper Kanat

Reputation: 396

Earlier in the code I was using the field with @JsonFormat annotation but removed that as I thought it was meant for serialization only like the JavaDocs suggest.

Turned out that I needed add back that annotation. And the real issue was that the 3rd party service response was indeed wrong (it was missing a wrapper element in the XML) which caused the deserialisation to fail. The error was:

com.fasterxml.jackson.databind.JsonMappingException: Can not instantiate value of type [simple type, class com.foo.bar.adapter.john.model.account.UserAccount] from String value ('2015-11-18T00:00:00.000+0200'); no single-String constructor/factory method

The field is written like below:

@JsonFormat(pattern = Constants.DATETIME_FORMAT)
@JacksonXmlProperty(localName = "transactionDate")
private ZonedDateTime transactionDateTime;

Also I had to add @JsonRootName("transaction") to the class of this field because the object is wrapped into a collection.

Upvotes: 4

Ricardo Vila
Ricardo Vila

Reputation: 1632

You can use annotations like:

@JsonSerialize(using = MyCustomJsonDateSerializer.class)

or

@JsonDeserialize(using = MyCustomJsonDateDeserializer.class)

To customize how Jackson parses Dates. Those custom Serializer and Deserializer must extend JsonSerializer and JsonDeserializer. For example:

public class MyCustomJsonDateSerializer extends JsonSerializer<Date> {

    @Override
    public void serialize(Date date, JsonGenerator jgen, SerializerProvider provider) throws IOException, JsonProcessingException {
        jgen.writeString(date != null ? ISODateTimeFormat.dateTime().print(new DateTime(date)) : null);
      }
}

Upvotes: 6

Related Questions