ZonedDateTime 的杰克逊反序列化问题

Posted

技术标签:

【中文标题】ZonedDateTime 的杰克逊反序列化问题【英文标题】:Jackson deserialization issue for ZonedDateTime 【发布时间】:2016-04-18 06:56:58 【问题描述】:

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

private ZonedDateTime transactionDateTime;

我正在使用的服务可能会使用以下模式返回 Date 或 DateTime:yyyy-MM-dd'T'HH:mm:ss.SSSZ

让我举两个服务返回的例子:

2015-11-18T18:05:38.000+0200 2015-11-18T00:00:00.000+0200

虽然第一个运行良好,但后者在反序列化过程中会引发以下异常:

java.time.format.DateTimeParseException:文本 '2015-11-18T00:00:00.000+0200' 无法在索引 23 处解析

我正在使用;

Spring Boot 1.3.1 Jackson 2.6.4(包含 JSR310 模块)

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

【问题讨论】:

【参考方案1】:

您可以使用如下注释:

@JsonSerialize(using = MyCustomJsonDateSerializer.class)

@JsonDeserialize(using = MyCustomJsonDateDeserializer.class)

自定义 Jackson 解析日期的方式。那些自定义的 Serializer 和 Deserializer 必须扩展 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);
      

【讨论】:

【参考方案2】:

在代码的早期,我使用带有 @JsonFormat 注释的字段,但删除了它,因为我认为它只是像 JavaDocs 建议的那样用于序列化。

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

com.fasterxml.jackson.databind.JsonMappingException: 不能 实例化类型的值 [简单类型,类 com.foo.bar.adapter.john.model.account.UserAccount] 来自字符串值('2015-11-18T00:00:00.000+0200');没有单串 构造函数/工厂方法

字段的写法如下:

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

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

【讨论】:

你能分享一下模式吗? Constants.DATETIME_FORMAT yyyy-MM-dd'T'HH:mm:ss.SSSZ 谢谢,但这对我不起作用,最终我通过为 ZonedDateTime 类编写自定义序列化器和反序列化器解决了我的问题。 你的类路径有 JSR310 依赖吗? 是的,我使用 ZonedDatetime。序列化工作正常,但反序列化结束 UTC zonedDateTime 对象【参考方案3】:

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

如果你在春天,这个功能可以被关闭

spring.jackson.deserialization.ADJUST_DATES_TO_CONTEXT_TIME_ZONE=false

【讨论】:

或者如果使用杰克逊映射器new ObjectMapper().registerModule(new JavaTimeModule()).disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)【参考方案4】:

我用过

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

加上jackson-datatype-jsr310 库,很明显。

Jackson deserialize ISO8601 formatted date-time into Java8 Instant中描述了这个解决方案

【讨论】:

【参考方案5】:

以下配置对我有帮助

指定日期时间模式:

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 配置方法的影响(如应用程序属性,或带有 Jackson2ObjectMapperBuilderCustomizer bean 定义的 @Configuration 类或其他方式)。所以RestTemplate的objectMapper(如果有的话)也要配置

【讨论】:

以上是关于ZonedDateTime 的杰克逊反序列化问题的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 不为 ZonedDateTime 使用自定义反序列化器

有啥方法可以防止杰克逊中的字段反序列化?

杰克逊:地图的反序列化

杰克逊:反序列化(解析)空Unicode

杰克逊不能反序列化空数组

JSON杰克逊序列化反序列化列表列表