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

时间:2016-08-22 18:35:42

标签: java json spring-boot jackson 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;
  }
}

在我的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;
}

我期待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", ...

我只有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/

4 个答案:

答案 0 :(得分:36)

有一个图书馆jackson-datatype-jsr310。试试吧。

此库涵盖新的日期时间API,并包含ZonedDateTime的序列化程序。

您只需添加JavaTimeModule

即可
ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());

更新

要将日期时间转换为ISO-8601字符串,您应该禁用WRITE_DATES_AS_TIMESTAMPS功能。您可以通过覆盖ObjectMapper bean或使用application properties

轻松完成
spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false

答案 1 :(得分:2)

如果您不依赖SpringBoot的自动配置功能-您不向配置文件中提供 this.markerForm = this.fb.group({ description: ['', Validators.required ] }); 属性-或出于任何原因您手动创建spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false实例。您可以通过编程方式禁用此功能,如下所示:

ObjectMapper

这是给杰克逊ObjectMapper m = new ObjectMapper(); m.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);

答案 2 :(得分:1)

上面已经提到了答案,但我认为它缺少一些信息。对于那些希望以多种形式解析Java 8时间戳的人(不仅仅是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中全局配置对象映射器以实现此目的。我还没有找到一种干净的方法来在不注册自定义(反)序列化器的情况下将其作为字段上的注释。

答案 3 :(得分:0)

对于杰克逊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>

模块pom.xml

<!-- https://github.com/FasterXML/jackson-modules-java8 -->
<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

JsonMapper的创建,可能在您的@Configuration类中

@Bean
public JsonMapper jsonMapper() {
    return JsonMapper.builder()
        .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
        .addModule(new JavaTimeModule())
        .build();
}

进一步阅读: