Spring Boot 问题使用 Jackson 序列化 java.time.LocalDateTime 以返回 ISO-8601 JSON 时间戳?

Posted

技术标签:

【中文标题】Spring Boot 问题使用 Jackson 序列化 java.time.LocalDateTime 以返回 ISO-8601 JSON 时间戳?【英文标题】:Spring Boot issues serializing java.time.LocalDateTime with Jackson to return ISO-8601 JSON timestamps? 【发布时间】:2016-12-02 09:24:05 【问题描述】:

我正在将 spring-boot REST API 应用程序中的一些模型转换为使用 java 8 的 java.time.LocalDateTime 而不是 joda 的 DateTime。我希望从 API 调用返回的时间戳遵循 ISO_8601 格式。动机是向前兼容 Java 8 的时代 (more here)。

证明困难的部分是将包含LocalDateTime 的对象序列化为JSON。

例如,我有以下实体:

// ... misc imports

import java.time.LocalDateTime;
import java.time.ZoneOffset;

@Data 
@Entity
@Table(name = "users")
public class User 

    @Id @Column
    private String id;

    @Column
    private String name;

    @Column
    private String email;

    @Column
    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "UTC")
    private java.time.LocalDateTime createdAt;

    public User(String name, String email) 
        this.id = Utils.generateUUID();
        this.createdAt = LocalDateTime.now(ZoneOffset.UTC);
    

我还设置了我的application.properties 以关闭日期作为时间戳杰克逊功能:

spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false

我的 Maven 部门:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>1.3.6.RELEASE</version>
</dependency>

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
    <version>2.8.1</version>
</dependency>

<dependency>
     <groupId>org.hibernate</groupId>
     <artifactId>hibernate-core</artifactId>
     <version>5.0.1.Final</version>
 </dependency>

最后,我尝试通过控制器检索 JSON 表示:

@RequestMapping("/users")
@RestController
public class UserController 

    private UserService userService;

    @Autowired
    public UserController(UserService userService) 
        this.userService = userService;
    

    @RequestMapping(
        value = "/id",
        method = RequestMethod.GET,
        produces = MediaType.APPLICATION_JSON_UTF8_VALUE
    )
    public User getUser(@PathVariable("id") String id) 
        return userService.findById(id);
    

当我实际调用此端点时,我得到以下异常:

java.lang.NoSuchMethodError: 
com.fasterxml.jackson.datatype.jsr310.ser.JSR310FormattedSerializerBase.findFormatOverrides(Lcom/fasterxml/jackson/databind/SerializerProvider;Lcom/fasterxml/jackson/databind/BeanProperty;Ljava/lang/Class;)Lcom/fasterxml/jackson/annotation/JsonFormat$Value;

另外,我还在配置类中配置了应用程序的ObjectMapper

@Configuration
public class ServiceConfiguration 

    @Bean
    public ObjectMapper getJacksonObjectMapper() 
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.registerModule(new JavaTimeModule());
        objectMapper.configure(
            com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS,
            false
        );
        return objectMapper;
    

任何线索将不胜感激。


更新:

原来是 Spring Boot 的 Jackson 版本与我的 pom.xml 中的版本不匹配。正如 Miloš 和 Andy 建议的那样,一旦我设置了正确的版本并使用 spring.jackson.serialization.write_dates_as_timestamps=true 运行应用程序,问题就解决了,无需配置 ObjectMapper 或在我的 LocalDateTime 模型字段上添加注释。

    ...
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>1.3.6.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>1.3.6.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
        </dependency>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-core</artifactId>
        </dependency>

    </dependencies>

【问题讨论】:

【参考方案1】:

NoSuchMethodError 是因为您正在混合 Jackson 的版本。 Spring Boot 1.3.6 使用 Jackson 2.6.7,而您使用的是 jackson-datatype-jsr310 的 2.8.1。

Spring Boot 为 Jackson 提供了依赖管理,包括 jackson-datatype-jsr310,所以你应该从你的 pom.xml 中删除该版本。如果你想使用不同版本的 Jackson,你应该覆盖 jackson.version 属性:

<properties>
    <jackson.version>2.8.1</jackson.version>
</properties>

这将确保您的所有 Jackson 依赖项具有相同的版本,从而避免版本不匹配的问题。

如果您愿意,您还可以删除配置ObjectMapper 的Java 代码。 Java Time 模块在类路径中时将自动注册,并且可以在application.properties 中配置将日期写入时间戳:

spring.jackson.serialization.write-dates-as-timestamps=false

【讨论】:

【参考方案2】:

您的 ObjectMapper bean 必须标记为 @Primary 才能被 Spring 拾取。或者,您可以只创建一个 JavaTimeModule bean,它会被 Spring 拾取并添加到默认对象映射器中。

你可能已经看过了,但是take a look at the official documentation。

【讨论】:

谢谢米洛什。不幸的是,我仍然遇到同样的异常。 我刚刚注意到您正在使用 1.3.6 的 Spring Boot 和 2.8 Jackson,但我认为它们不兼容。您可以尝试使用 Boot 1.4 吗? 或者只使用 Boot 1.3.6 附带的相同版本的 jsr310 Jackson - 我认为应该是 2.6.7【参考方案3】:

发生错误是因为您混合了 Jackson 的版本。您正在使用 Spring Boot 的 1.3.6.RELEASE 版本。如果您要迁移到 Spring Boot 版本 2.x.x.RELEASE,那么您可以将 com.fasterxml.jackson.datatype 依赖替换为 spring-boot-starter-json 依赖。这样一来,您就可以让 Spring Boot 处理正确的 Jackson 版本。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-json</artifactId>
</dependency>

【讨论】:

从 spring boot 1.3.6 迁移到 2.x.x 可能会产生很大的影响,并且是一个糟糕的建议。

以上是关于Spring Boot 问题使用 Jackson 序列化 java.time.LocalDateTime 以返回 ISO-8601 JSON 时间戳?的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot没有使用Jackson Kotlin插件

Jackson 在我的 Spring Boot 应用程序中忽略了 spring.jackson.properties

如何在 Spring Boot 1.4 中自定义 Jackson

尝试通过 Spring Boot Rest 使用 Jackson 验证 JSON

在 Spring Boot 中使用 Jackson 反序列化 Date 对象

使用 Spring Boot 和 Jackson 的日期时区