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 的杰克逊反序列化问题的主要内容,如果未能解决你的问题,请参考以下文章