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