首页 > 解决方案 > 使用 Spring Boot 和 Jackson ObjectMapper 时 OffsetDateTime 属性 JSON 序列化的区别

问题描述

当我开始为基于 Spring Boot 的 REST API 编写测试时,我注意到OffsetDateTime无论我使用 Spring MockMVC 执行请求还是使用 Jackson 来序列化 DTO 中的属性都不同ObjectMapper。使用 Spring 时,我的@JSONFormat注释使用正确,但使用时却ObjectMapper没有。

@EqualsAndHashCode
@Builder
public class FooDTO{
    public int id;
    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm:ss")
    public OffsetDateTime arrival;

    public fooDTO(int id, OffsetDateTime arrival){
        this.id = id;
        this.arrival = arrival;
    }
}

@RestController
public class FooController {
    @Autowired
    private FooRepository fooRepository;

    @RequestMapping(value = "/foo/bar/{id}")
    public fooDTO getFoo (@PathVariable int id) {
        return fooRepository.loadDTO(id);
    }
}

@RunWith(SpringRunner.class)
@ContextConfiguration(classes = Application.class)
@WebMvcTest(FooController.class)
public class FooControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private FooRepository fooRepository;

    @Test
    public void fooTest() {
        FooDTO fooDTO = FooDTO.builder().id(1).arrival(OffsetDateTime.now()).build();
        String fooDTOJSON = new ObjectMapper().writeValueAsString(fooDTO);
        when(fooRepository.loadDTO(1).thenReturn(fooDTO);
        String reponse = mockMvc.perform(request(HttpMethod.GET, "/foo/bar/1").accept(APPLICATION_JSON).andReturn().getResponse().getContentAsString();
        assertEquals(fooDTOJSON, response);                                                               
    }

Spring MockMVC 响应如下所示:

{"id":1, "arrival": "2020-03-28 12:29:44"}

虽然 ObjectMapper 中的 fooDTOJSON 看起来像这样:

{"id":1, "arrival":{"offset":{"totalSeconds":3600,"id":"+01:00","rules":{"fixedOffset":true,"transitions":[],"transitionRules":[]}},"nano":697162400,"year":2020,"monthValue":3,"dayOfMonth":28,"hour":12,"minute":29,"second":44,"dayOfWeek":"SATURDAY","dayOfYear":88,"month":"MARCH"}}

理想情况下,我希望ObjectMapper返回与 MockMVC 相同的结果并在 DTO 上使用我的注释。即使解决方案可能很明显,我也非常感谢有人对此提供帮助。我不太习惯在 Java 生态系统中工作,尤其是 Spring。

标签: javaspringspring-bootdatetimejackson

解决方案


导致预期结果的解决方案是添加jackson-modules-java8库(在我的情况下,由于 Spring(-Boot) 或我的应用程序的另一个依赖项,我认为它已经存在)并将相应的模块添加到 Jackson Mapper . 接收“正确” JSON 字符串的工作解决方案是:

String fooDTOJSON = new ObjectMapper().registerModule(new JavaTimeModule()).writeValueAsString(fooDTO);

推荐阅读