Java Jackson在Spring Boot中错误地序列化了ZoneDateTime

Java Jackson在Spring Boot中错误地序列化了ZoneDateTime,java,json,spring-boot,jackson,zoneddatetime,Java,Json,Spring Boot,Jackson,Zoneddatetime,我有一个带有Spring Boot和Jetty的简单应用程序。我有一个简单的控制器,它返回一个对象,该对象具有Java 8ZoneDateTime: public class Device { // ... private ZonedDateTime lastUpdated; public Device(String id, ZonedDateTime lastUpdated, int course, double latitude, double longitude) {

我有一个带有Spring Boot和Jetty的简单应用程序。我有一个简单的控制器,它返回一个对象,该对象具有Java 8
ZoneDateTime

public class Device {
  // ...
  private ZonedDateTime lastUpdated;

  public Device(String id, ZonedDateTime lastUpdated, int course, double latitude, double longitude) {
    // ...
    this.lastUpdated = lastUpdated;
    // ...
  }

  public ZonedDateTime getLastUpdated() {
    return lastUpdated;
  }
}
在我的
RestController
中,我只有:

@RequestMapping("/devices/")
public @ResponseBody List<Device> index() {
  List<Device> devices = new ArrayList<>();
  devices.add(new Device("321421521", ZonedDateTime.now(), 0, 39.89011333, 24.438176666));

  return devices;
}
我只有一个
springbootstarterweb
应用程序,使用
springbootstarterjetty
并排除
springbootstartertomcat

为什么杰克逊在春天的靴子里会这样

**更新**

对于那些想要一个完整的分步指南如何解决这个问题的人,我问了一个问题后发现: 有一个图书馆。试试看

该库涵盖了新的datetime API,还包括
ZoneDateTime
的序列化程序

您只需添加
JavaTimeModule

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
更新

若要将datetime转换为字符串,应禁用该功能。您可以通过重写
ObjectMapper
bean或使用以下方法轻松完成:


如果您不依赖SpringBoot的自动配置功能-您不向配置文件提供
spring.jackson.serialization.WRITE_DATES\u AS_timestaps=false
属性-或者出于任何原因手动创建
ObjectMapper
实例。您可以按如下方式通过编程禁用此功能:

ObjectMapper m = new ObjectMapper();
m.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

这是给jackson的
2.8.7

上面已经提到了答案,但我认为它缺少一些信息。对于那些希望以多种形式解析Java8时间戳的人(不仅仅是ZonedDateTime)。您的POM中需要最新版本的
jackson-datatype-jsr310
,并注册以下模块:

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
要测试此代码

@Test
void testSeliarization() throws IOException {
    String expectedJson = "{\"parseDate\":\"2018-12-04T18:47:38.927Z\"}";
    MyPojo pojo = new MyPojo(ZonedDateTime.parse("2018-12-04T18:47:38.927Z"));

    // serialization
    assertThat(objectMapper.writeValueAsString(pojo)).isEqualTo(expectedJson);

    // deserialization
    assertThat(objectMapper.readValue(expectedJson, MyPojo.class)).isEqualTo(pojo);
}

请注意,您可以在Spring或dropwizard中全局配置对象映射器以实现此目的。我还没有找到一种干净的方法,可以在不注册自定义(反)序列化程序的情况下将其作为字段上的注释来执行。

对于Jackson
2.10及更高版本

父pom.xml

<!-- https://github.com/FasterXML/jackson-bom -->
<dependencyManagement>
  <dependency>
    <groupId>com.fasterxml.jackson</groupId>
    <artifactId>jackson-bom</artifactId>
    <version>2.10.3</version>
    <type>pom</type>
    <scope>import</scope>
  </dependency>
</dependencyManagement>
<!-- https://github.com/FasterXML/jackson-modules-java8 -->
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
进一步阅读:


谢谢。因此,我添加了
jackson-datatype-jsr310
version2.8.1依赖项,其效果是时间戳更改为一个双
“lastUpdated”:1471893818.177000000
。在Spring Boot中,我无法直接访问它正在使用的
ObjectMapper
,我能告诉它在哪里使用
JavaTimeModule
?(我对Spring Boot有点陌生)@jbx检查这个答案-你还需要尝试关闭
WRITE_DATES\u AS_TIMESTAMPS
功能,将datetime转换为ISO-8601。我正要告诉你我找到了答案。是的,它可以工作,只需通过
spring.jackson.serialization.WRITE_DATES\u AS_TIMESTAMPS=false
,无需通过编程自定义
ObjectMapper
。因为我的问题是关于Spring boot的,你能编辑你的答案来添加这个吗?这样我就选择它作为正确的答案了?@jbx太好了!很高兴你彻底解决了。谢谢,我会编辑我的回答谢谢!这很有帮助。如果你真的想让事情变得简单,而不是让下一个读你代码的人绊倒,你只需要自己把你的列表转换成一个列表,然后把格式正确的字符串放在你想要的字段中。这样你就不必熟悉杰克逊不断变化的魔法运作方式。我知道如果设备有很多字段,这可能不是一个选项,但我只是想浮动选项:)。如果你使用Spring Boot,一半的东西是神奇的,如果配置错误,可能会让你绊倒。自从我提出这个问题以来,JSR310配置已经成为Java 8时间支持的“标准”样板。也许将来Jackson库会默认这么做。我的只是包含一个测试,演示如何使用映射器。我知道答案不多,但我自己编写了代码来测试解决方案,因此我想我最好添加它,以防其他人感到疑惑。这与其他答案有什么不同?从Jackson
2.10
及以上
ObjectMapper
不再推荐。这个答案是最新的,有新的类(例如
JsonMapper
)和方法(例如
addModule
)。谢谢更新。实际上,从
application.yml
更改属性
spring.jackson.serialization.WRITE\u DATES\u作为\u TIMESTAMPS=false
就足够了。
<!-- https://github.com/FasterXML/jackson-bom -->
<dependencyManagement>
  <dependency>
    <groupId>com.fasterxml.jackson</groupId>
    <artifactId>jackson-bom</artifactId>
    <version>2.10.3</version>
    <type>pom</type>
    <scope>import</scope>
  </dependency>
</dependencyManagement>
<!-- https://github.com/FasterXML/jackson-modules-java8 -->
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>
@Bean
public JsonMapper jsonMapper() {
    return JsonMapper.builder()
        .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
        .addModule(new JavaTimeModule())
        .build();
}