【问题标题】:Force Jackson serialize LocalDate to Array强制杰克逊将 LocalDate 序列化为数组
【发布时间】:2020-10-06 22:34:52
【问题描述】:

我正在使用 spring-boot 2.1.6,并且有一个 API 可以接受包含如下日期的表单:

@Data
public class MyForm {
    private LocalDate date;
    ...
}

@Controller
public class MyController {
    @PostMapping("...")
    public ResponseEntity<...> post(@RequestBody MyForm myForm) {
        ...
    }
}

spring MVC 默认接受这种 JSON 格式:

{
   "date": [2020, 6, 17],
   ...
}

所以在前端,我的 JavaScript 代码只需提交一个这样的表单,即 JS 会将日期转换为数组。

但是当我运行 spring-boot 测试时,这个序列化不起作用,代码如下:

    private ObjectMapper mapper = new ObjectMapper();

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void doTest() {
        MyForm form = ...
        MvcResult result = mockMvc.perform(MockMvcRequestBuilders.post("/...").
                contentType("application/json").content(mapper.writeValueAsString(form)).andReturn();
        ...
    }

这是因为 Jackson 默认将 LocalDate 序列化为:

{
    "date": {
        "year":2020,
        "month":"JUNE",
        "monthValue":6,
        ...
    }
    ...
}

这里提到:LocalDate Serialization: date as array?,有很多配置可以强制 spring-boot 将数据序列化为格式yyyy-MM-dd。但我不想更改我的 JS 代码。我只是想让我的测试用例工作。

如何配置ObjectMapper 以强制Jackson 将LocalDate 序列化为数组?我只想得到这个:

{
   "date": [2020, 6, 17],
   ...
}

更新

LocalDate 这里是java.time.LocalDate 但不是org.joda.time.LocalDate

【问题讨论】:

  • 您可以尝试从.json 文件中读取.content(...)。一种解决方法,而不是为测试编写自定义序列化程序。
  • 但在我的测试用例中,MyForm 是由一堆 Java 代码生成的 :-(

标签: java json spring date jackson


【解决方案1】:

您需要注册JavaTimeModule。 Maven 依赖:

<dependency>
    <groupId>com.fasterxml.jackson.datatype</groupId>
    <artifactId>jackson-datatype-jsr310</artifactId>
</dependency>

例如,如何使用它:

import com.fasterxml.jackson.databind.json.JsonMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;

import java.time.LocalDate;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        JsonMapper mapper = JsonMapper.builder()
                .addModule(new JavaTimeModule())
                .build();
        mapper.writeValue(System.out, new MyForm());
    }
}

class MyForm {

    private LocalDate value = LocalDate.now();

    public LocalDate getValue() {
        return value;
    }

    public void setValue(LocalDate value) {
        this.value = value;
    }
}

上面的代码打印:

{"value":[2020,6,17]}

另见:

【讨论】:

    【解决方案2】:

    您可以尝试为LocalDate 创建一个自定义反序列化器

    class LocalDateDeserializer extends StdDeserializer<LocalDate> {
        @Override
        public LocalDate deserialize(JsonParser parser, DeserializationContext context)
                    throws IOException, JsonProcessingException {
           // implement;
        }
    }
    

    然后通过添加Module bean 来注册它。来自documentation

    com.fasterxml.jackson.databind.Module 类型的任何 bean 都会自动注册到自动配置的 Jackson2ObjectMapperBuilder 并应用于它创建的任何 ObjectMapper 实例。当您向应用程序添加新功能时,这提供了一种用于贡献自定义模块的全局机制。

    @Bean
    public Module LocalDateDeserializer() {
        SimpleModule module = new SimpleModule();
        module.addDeserializer(LocalDate.class, new LocalDateDeserializer());
        return module;
    }
    

    【讨论】:

      【解决方案3】:

      您可以构建一个转换器来获取日期值并返回所需的数组。 这将是你的实体

      @JsonSerialize(converter=DateToArray.class)
       private LocalDate date;
      

      你的转换器

          @Component
          public class DateToArray extends StdConverter< Date, String[]> {
      
            @Override
            public String[] convert(Date value) {
                //logic for pushing data into Array and return it
            }
          }
      

      【讨论】:

        猜你喜欢
        • 2017-11-09
        • 2021-05-27
        • 1970-01-01
        • 1970-01-01
        • 2018-02-01
        • 1970-01-01
        • 1970-01-01
        • 2017-07-01
        • 1970-01-01
        相关资源
        最近更新 更多