Jackson jsr310中缺少ZonedDateTimeDeserializer [英] ZonedDateTimeDeserializer is missing in jackson jsr310
问题描述
我正在使用以下方式解析 ZonedDateTime
:
I'm parsing a ZonedDateTime
using like this:
@JsonSerialize(using = ZonedDateTimeSerializer.class)
private ZonedDateTime expirationDateTime;
我需要能够正确反序列化此日期。但是,jackson没有提供解串器:
I need to be able to properly deserialize this date. However, there is no deserializer for this that is provided by jackson:
com.fasterxml.jackson.datatype.jsr310.deser
有没有理由错过它?什么是最常见的解决方法?
Is there a reason why it's missing? What is the most common workaround?
更新:
这是我的方案:
Updated: Here is my scenario:
我创建 ZonedDateTime
像这样:
ZonedDateTime.of(2017, 1, 1, 1, 1, 1, 1, ZoneOffset.UTC)
然后我序列化包含这样的日期:
Then I serialize the object that contains the date like this:
public static String toJSON(Object o) {
ObjectMapper objectMapper = new ObjectMapper();
StringWriter sWriter = new StringWriter();
try {
JsonGenerator jsonGenerator = objectMapper.getJsonFactory().createJsonGenerator(sWriter);
objectMapper.writeValue(jsonGenerator, o);
return sWriter.toString();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
当我尝试将其发送到Spring MVC Controller时:
And when I try to send it to Spring MVC Controller:
mockMvc.perform(post("/endpoint/")
.content(toJSON(myObject))
.contentType(APPLICATION_JSON))
.andExpect(status().isOk());
进入控制器内部的日期对象是不同的。
The date object that goes inside the controller is different.
之前: 2017-01-01T01:01:01.000000001Z
之后: 2017-01-01T01:01:01.000000001Z [UTC]
推荐答案
2个值 2017-01-01T01:01:01.000000001Z
和 2017-01-01T01:01:01.000000001 Z [UTC]
实际上代表相同的瞬间,因此它们是等效的,可以毫无问题地使用(至少应该没有问题,因为它们代表同一时刻) 。
The 2 values 2017-01-01T01:01:01.000000001Z
and 2017-01-01T01:01:01.000000001Z[UTC]
actually represent the same instant, so they are equivalent and can be used without a problem (at least there should be no problems as they represent the same instant).
唯一的细节是杰克逊出于某种原因,在反序列化时将 ZoneId
值设置为UTC ,在这种情况下是多余的( Z
已经告诉偏移是UTC)。但它不应该影响日期值本身。
The only detail is that Jackson, for some reason, sets the ZoneId
value to "UTC" when deserializing, which is redundant in this case (the Z
already tells that the offset is "UTC"). But it shouldn't affect the date value itself.
一种非常简单的方法来摆脱这个 [UTC]
部分是将此对象转换为 OffsetDateTime
(因此它保持 Z
偏移,不要使用 [UTC]
区域,然后再返回 ZonedDateTime
:
A very simple way to get rid of this [UTC]
part is to convert this object to OffsetDateTime
(so it keeps the Z
offset and don't use the [UTC]
zone) and then back again to ZonedDateTime
:
ZonedDateTime z = // object with 2017-01-01T01:01:01.000000001Z[UTC] value
z = z.toOffsetDateTime().toZonedDateTime();
System.out.println(z); // 2017-01-01T01:01:01.000000001Z
之后,<$ c的值$ c> z 变量将 2017-01-01T01:01:01.000000001Z
(不含 [UTC]
part)。
After that, the value of z
variable will be 2017-01-01T01:01:01.000000001Z
(without the [UTC]
part).
但当然这并不理想,因为你必须手动完成所有日期。一个更好的方法是编写一个自定义反序列化器(通过扩展 com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer
),当它 UTC :
But of course this is not ideal as you'd have to do it manually for all dates. A better approach is to write a custom deserializer (by extending com.fasterxml.jackson.datatype.jsr310.deser.InstantDeserializer
) that don't set the timezone when it's UTC:
public class CustomZonedDateTimeDeserializer extends InstantDeserializer<ZonedDateTime> {
public CustomZonedDateTimeDeserializer() {
// most parameters are the same used by InstantDeserializer
super(ZonedDateTime.class,
DateTimeFormatter.ISO_ZONED_DATE_TIME,
ZonedDateTime::from,
// when zone id is "UTC", use the ZoneOffset.UTC constant instead of the zoneId object
a -> ZonedDateTime.ofInstant(Instant.ofEpochMilli(a.value), a.zoneId.getId().equals("UTC") ? ZoneOffset.UTC : a.zoneId),
// when zone id is "UTC", use the ZoneOffset.UTC constant instead of the zoneId object
a -> ZonedDateTime.ofInstant(Instant.ofEpochSecond(a.integer, a.fraction), a.zoneId.getId().equals("UTC") ? ZoneOffset.UTC : a.zoneId),
// the same is equals to InstantDeserializer
ZonedDateTime::withZoneSameInstant, false);
}
}
然后你必须注册这个解串器。如果你使用 ObjectMapper
,你需要将它添加到 JavaTimeModule
:
Then you have to register this deserializer. If you use ObjectMapper
, you need to add this to the JavaTimeModule
:
ObjectMapper objectMapper = new ObjectMapper();
JavaTimeModule module = new JavaTimeModule();
// add my custom deserializer (this will affect all ZonedDateTime deserialization)
module.addDeserializer(ZonedDateTime.class, new CustomZonedDateTimeDeserializer());
objectMapper.registerModule(module);
如果在Spring中配置它,配置将是这样的(未测试):
If you configure it in Spring, the config will be something like this (not tested):
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean" id="pnxObjectMapper">
<property name="deserializersByType">
<map key-type="java.lang.Class">
<entry>
<key>
<value>java.time.ZonedDateTime</value>
</key>
<bean class="your.app.CustomZonedDateTimeDeserializer">
</bean>
</entry>
</map>
</property>
</bean>
这篇关于Jackson jsr310中缺少ZonedDateTimeDeserializer的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!