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

d5vmydt9  于 2022-11-08  发布在  其他
关注(0)|答案(5)|浏览(134)

我有一个简单的应用程序,其中包含Sping 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
为什么Jackson在春 Boot 里会这样?

更新

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

nszi6y05

nszi6y051#

有一个库jackson-datatype-jsr310。试试看。
此库涵盖了新的datetime API,还包括用于ZonedDateTime的序列化程序。
您只需要添加JavaTimeModule即可:

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

要将日期时间转换为ISO-8601字符串,您应该禁用WRITE_DATES_AS_TIMESTAMPS功能。您可以通过覆盖ObjectMapper bean或使用应用程序属性轻松地完成此操作:

spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false
6tdlim6h

6tdlim6h2#

如果你不依赖SpringBoot的自动配置特性--你没有在配置文件中提供spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS = false属性--或者出于某种原因你手动创建了ObjectMapper示例。你可以通过编程方式禁用这个特性,如下所示:

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

这是给Jackson2.8.7

qyzbxkaa

qyzbxkaa3#

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

efzxgjgh

efzxgjgh4#

对于Jackson2.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();
}

进一步阅读:

wa7juj8i

wa7juj8i5#

application.yml中的spring.jackson.serialization.write-dates-as-timestamps=false设置在我们的项目中没有帮助。很可能是因为有其他库在使用Jackson:Swagger / OpenAPI / OpenAPI生成器。
RequestMappingHandlerAdapter的这个@EventListener添加到@SpringBootApplication类中会有所帮助。

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;

@SpringBootApplication
public class SpringBootInitializer {

    @Autowired
    private RequestMappingHandlerAdapter handlerAdapter;

    public static void main(String[] args) {
        SpringApplication.run(SpringBootInitializer.class, args);
    }

    @EventListener
    public void handleContextRefresh(ContextRefreshedEvent event) {
        // see https://github.com/FasterXML/jackson-modules-java8/issues/11#issuecomment-913199874
        // spring.jackson.serialization.write-dates-as-timestamps=false setting does not work in our configuration (probably because of Swagger / OpenAPI / OpenAPI Generator libraries used)

        handlerAdapter
            .getMessageConverters()
            .forEach(c -> {
                if (c instanceof MappingJackson2HttpMessageConverter jsonMessageConverter) {
                    ObjectMapper objectMapper = jsonMessageConverter.getObjectMapper();
                    objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
                }
            });
    }
}

我在这里找到了这个解决方案-https://github.com/FasterXML/jackson-modules-java8/issues/11#issuecomment-913199874。

相关问题