使用 Jackson JSR310 模块反序列化 LocalDateTime

Posted

技术标签:

【中文标题】使用 Jackson JSR310 模块反序列化 LocalDateTime【英文标题】:Deserializing LocalDateTime with Jackson JSR310 module 【发布时间】:2015-06-16 18:31:42 【问题描述】:

我正在使用the Jackson Datatype JSR310 page 描述的库,但我仍然难以让它工作。

我已经配置了以下bean:

@Bean
@Primary
public ObjectMapper objectMapper() 
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JSR310Module());
    return mapper;

当我调用我的 REST API 时,日期格式输出为 yyyy-MM-dd'T'HH:ss.SSSSSS,例如2015-04-11T00:10:38.905847。这可以由我的 AngularJS 代码处理。

当我想向 REST API 提交内容时,日期会发布为 yyyy-MM-dd'T'HH:mm:ss.SSS'Z',例如2015-04-09T08:30:00.000Z

杰克逊最后一直在抱怨“Z”。如果我查看文档中的LocalDateTimeDeserializer,它使用DateTimeFormatter.ISO_LOCAL_DATE_TIME,它归结为ISO_LOCAL_DATE'T'ISO_LOCAL_TIME,并提到它没有覆盖区域。

所以我想我应该在我正在创建的ObjectMapper 上设置DateFormat

@Bean
@Primary
public ObjectMapper objectMapper() 
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(new JSR310Module());
    mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"));
    return mapper;

但这无济于事。我将其更改为yyyy-MM-dd 之类的简单名称,但序列化日期仍为以前的格式,反序列化也不受影响。

我在这里做错了什么才能使其正常工作?据我所知,我的 javascript 代码中的日期格式是 ISO 8601 格式...

【问题讨论】:

我认为如果你切换到 ZoneDateTime 可能会起作用。 @AlexeyGavrilov,这确实有效,谢谢。如果您将其作为答案提交,我会将其标记为正确。仍然觉得奇怪的是,在 objectmapper 本身上设置 dateFormat 对序列化/反序列化绝对没有影响,但使用 ZonedDateTime 很容易:-) ObjectMapper 的 DateFormat 仅用于内置 java.util.Date 支持,我猜。将带有时区指示的值传递给 LocalDateTime 是一种气味- Instant 或 OffsetDateTime(甚至 ZonedDateTime)更适用。 【参考方案1】:

Hibernate 4、Spring 4 - REST WS、客户端 - Spring Boot 1.5.2。在我的例子中,我在实体 ZonedDateTime 类中使用了将 Timestamp 映射到数据库中。 Hibernate 和 Spring Boot REST 工作正常。我只能将库添加到 pom 文件中:

    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>$jackson.version</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 -->
    <dependency>
        <groupId>com.fasterxml.jackson.datatype</groupId>
        <artifactId>jackson-datatype-jsr310</artifactId>
        <version>$jackson.version</version>
    </dependency>

所以我想,该转换器也是在 Spring 中为 LocalDateTime 实现的。jackson.version 是最新的。

【讨论】:

OP 提到他们已经在使用 Jackson JSR 310 模块(否则 JSR310Module 类将不可用)。 是的,当然,但我没有更多的事情可做。不需要对象映射器,也不需要注册模块。【参考方案2】:

不必编写自己的序列化程序。使用默认格式就足够了,但是使用另一种格式(time_zone 格式)创建一个实例,以便剪切超出部分:

new LocalDateTimeDeserializer(DateTimeFormatter.ISO_DATE_TIME)

就我而言,我有一个像这样的contextResolver 可以在配置级别实现:

@Service 
@Provider
public class ObjectMapperContextResolver implements ContextResolver<ObjectMapper>   
    private final ObjectMapper mapper;

    public ObjectMapperContextResolver() 
        mapper = new ObjectMapper();
        JavaTimeModule javaTimeModule=new JavaTimeModule();
        // Hack time module to allow 'Z' at the end of string (i.e. javascript json's) 
        javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ISO_DATE_TIME));
        mapper.registerModule(javaTimeModule);
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
    

    @Override
    public ObjectMapper getContext(Class<?> type) 
        return mapper;
      

【讨论】:

我相信这个答案更合适,因为它演示了如何设置 ObjectMapper 以自动正确地处理反序列化。如果您不手动使用 ObjectMapper intsance 读取传入的字符串,而是通过诸如 RestTemplate 之类的东西来工作,则这是必需的 嗯。尝试此操作时出现此异常: java.lang.IllegalAccessError: 试图访问方法 com.fasterxml.jackson.databind.ser.std.StdSerializer.<init>(Ljava/lang/Class;& #41;来自 com.fasterxml.jackson.datatype.jsr310.JavaTimeModule 类的 V 你能看看***.com/questions/44918021/…吗?是不是很相似?如果可能请回答。【参考方案3】:

目前LocalDateTimeDeserializer 似乎不遵守为对象映射器设置的日期格式。

要使其正常工作,您可以覆盖 LocalDateTimeDeserializer 或切换到使用 ZoneDateTime 来处理末尾的“Z”字符。

这是一个例子:

public class Java8DateFormat 
    public static void main(String[] args) throws IOException 
        final ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JSR310Module());
        // mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"));

        final String date = mapper.writeValueAsString(new Date());
        System.out.println(date);
        System.out.println(mapper.readValue(date, ZonedDateTime.class));
    

输出:

"2015-04-11T18:24:47.815Z"
2015-04-11T18:24:47.815Z[GMT]

【讨论】:

请注意JSR310Module 已被弃用,请改用JavaTimeModule 您可以在这里找到新模块:github.com/FasterXML/jackson-modules-java8。

以上是关于使用 Jackson JSR310 模块反序列化 LocalDateTime的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot Jackson JSR-310 - ZonedDateTime 总是时间戳

在春季使用 JSR310 java.time 时将日期、即时序列化为 ISO 8601

如何正确地在Spring Data JPA和Jackson中用上Java 8的时间相关API(即JSR 310也即java.time包下的众神器)

万恶的Jackson

使用 Jackson 进行 DateTime 反序列化的默认时区(Joda-Time 模块)

无法使用 Jackson 将 java.time.LocalDate 序列化为字符串