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

jbx*_*jbx 27 java json jackson spring-boot zoneddatetime

我有一个Spring Boot和Jetty的简单应用程序.我有一个简单的控制器返回一个具有Java 8的对象ZonedDateTime:

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;
  }
}
Run Code Online (Sandbox Code Playgroud)

在我的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;
}
Run Code Online (Sandbox Code Playgroud)

我期望ZonedDateTime根据ISO格式进行格式化,但是我得到了类的整个JSON转储:

"lastUpdated":{"offset":{"totalSeconds":7200,"id":"+02:00","rules":{"fixedOffset":true,"transitionRules":[],"transitions":[]}},"zone":{"id":"Europe/Berlin","rules":{"fixedOffset":false,"transitionRules":[{"month":"MARCH","timeDefinition":"UTC","standardOffset":{"totalSeconds":3600,"id":"+01:00","rules":{"fixedOffset":true,"transitionRules":[],"transitions":[]}},"offsetBefore":{"totalSeconds":3600,"id":"+01:00","rules":{"fixedOffset":true,"transitionRules":[],"transitions":[]}},"offsetAfter":{"totalSeconds":7200,"id":"+02:00", ...
Run Code Online (Sandbox Code Playgroud)

我只是有一个spring-boot-starter-web应用程序,使用spring-boot-starter-jetty和排除spring-boot-starter-tomcat.

杰克逊为什么在Spring Boot中表现得像这样?

**更新**

对于那些寻求完整的一步一步指导如何解决这个问题的人,我在问了这个问题后发现了这个问题:http: //lewandowski.io/2016/02/formatting-java-time-with-spring-boot-using-json/

vsm*_*kov 43

有一个库jackson-datatype-jsr310.试试吧.

该库涵盖了新的datetime API,并且还包含序列化程序ZonedDateTime.

您只需要添加JavaTimeModule:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());
Run Code Online (Sandbox Code Playgroud)

UPDATE

要将datetime转换为ISO-8601字符串,您应该禁用WRITE_DATES_AS_TIMESTAMPS功能.您可以通过覆盖ObjectMapperbean或使用应用程序属性轻松完成:

spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false
Run Code Online (Sandbox Code Playgroud)

  • 谢谢。所以我添加了`jackson-datatype-jsr310` 2.8.1版本的依赖,效果是时间戳变成了双`"lastUpdated":1471893818.177000000`。在 Spring Boot 中,我无法直接访问它正在使用的“ObjectMapper”,知道在哪里告诉它使用“JavaTimeModule”吗?(我对 Spring Boot 有点陌生) (2认同)

bpa*_*ski 7

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

ObjectMapper m = new ObjectMapper();
m.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
Run Code Online (Sandbox Code Playgroud)

这是给杰克逊的 2.8.7


Ult*_*pon 5

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

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new JavaTimeModule());
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
Run Code Online (Sandbox Code Playgroud)

要测试此代码

@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);
}
Run Code Online (Sandbox Code Playgroud)

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