ZonedDateTime 的 Jackson 反序列化问题

Jackson deserialization issue for ZonedDateTime

我在反序列化我正在使用的服务期间使用的 class 中有以下字段。

private ZonedDateTime transactionDateTime;

我使用的服务可能 return 日期或日期时间使用模式:yyyy-MM-dd'T'HH:mm:ss.SSSZ

让我举两个例子说明服务 returns:

虽然第一个运行良好,但后者导致在反序列化过程中抛出以下异常:

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

我正在使用;

这是否需要自定义反序列化class?

您可以使用如下注释:

@JsonSerialize(using = MyCustomJsonDateSerializer.class)

@JsonDeserialize(using = MyCustomJsonDateDeserializer.class)

自定义 Jackson 解析日期的方式。这些自定义序列化器和反序列化器必须扩展 JsonSerializer 和 JsonDeserializer。例如:

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);
      }
}

在代码的前面,我使用带有 @JsonFormat 注释的字段,但删除了它,因为我认为它仅用于序列化,就像 JavaDocs 所建议的那样。

原来我需要加回那个注释。真正的问题是第 3 方服务响应确实是错误的(它在 XML 中缺少包装元素)导致反序列化失败。错误是:

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

字段写法如下:

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

我还必须将 @JsonRootName("transaction") 添加到该字段的 class 中,因为该对象被包装到一个集合中。

Jackson 反序列化将默认绕过时区信息并使用 ctx 时区覆盖它,所有 ISO8601 将以 UTC 结束

如果您使用 spring

,可以通过 关闭此功能
spring.jackson.deserialization.ADJUST_DATES_TO_CONTEXT_TIME_ZONE=false

我用过

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

加上 jackson-datatype-jsr310 库,显然。

中描述了此解决方案

以下配置对我有帮助

指定日期时间模式:

public class Timestamp {

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

}

禁止将 ZonedDateTime 转换为 UTC:

objectMapper.configure(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE, false);

还有另一个问题,可能很难发现。在我的例子中,我使用 RestTemplate 从另一个微服务接收 Timestamp。 RestTemplate 可以配置为使用 notProjectDefaultObjectMapper,这不受默认 Spring Jackson 配置方法的影响(如应用程序属性,或 @Configuration 类 with Jackson2ObjectMapperBuilderCustomizer bean 定义或其他方式)。所以RestTemplate的objectMapper(如果有的话)也应该配置