1. 概要
当使用 JSON 格式作为 HTTP 请求协议时,Spring Boot将使用 ObjectMapper 对象序列化 HTTP Response 并反序列化 HTTP Request 。 在本教程中,我们将介绍最常用序列化配置方法。
要学习更多关于 Jackson 知识,请跳转到我们的 Jackson 教程.
2. 默认配置
Spring Boot 默认配置为:
- Disable MapperFeature.DEFAULT_VIEW_INCLUSION
- Disable DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
- Disable SerializationFeature.WRITE_DATES_AS_TIMESTAMPS
让我们以一个例子开始:
- 客户端将向我们发送GET请求
/coffee?name=Lavazza
- Controller 返回 Coffee 对象
- Spring 使用 ObjectMapper 将 Coffee 对象 序列化为 JSON 字符串
下面我们将使用String和LocalDateTime对象,举例说明如何自定义序列化配置:
public class Coffee {
private String name;
private String brand;
private LocalDateTime date;
//getters and setters
}
为了演示,我们还需要定义一个REST Controller
@GetMapping("/coffee")
public Coffee getCoffee(
@RequestParam(required = false) String brand,
@RequestParam(required = false) String name) {
return new Coffee()
.setBrand(brand)
.setDate(FIXED_DATE)
.setName(name);
}
默认配置下,请求 http://lolcahost:8080/coffee?brand=Lavazza 返回的结果是:
{
"name": null,
"brand": "Lavazza",
"date": "2020-11-16T10:21:35.974"
}
现在我们不想要值为null的字段,并希望时间格式化为dd-MM-yyyy HH:mm
形式。 最终返回结果应该是这样的:
{
"brand": "Lavazza",
"date": "04-11-2020 10:34"
}
Spring Boot中,我们可以自定义Spring默认的 ObjectMapper 配置,或定义一个新的ObjectMapper直接覆盖掉它。我们将在下面中介绍这2种方法。
3. 自定义默认的 ObjectMapper
在本节中,我们将看到如何自定义Spring Boot默认的 ObjectMapper 。
3.1. Application Properties & 自定义 Jackson 模块
最简单的方法是通过修改application.properties来自定义mapper配置。 配置的一般结构是 :
spring.jackson.<category_name>.<feature_name>=true,false
例如,如果我们要禁用 SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, 则添加:
spring.jackson.serialization.write-dates-as-timestamps=false
除了上面,我们还可以配置其他:
spring.jackson.default-property-inclusion=always, non_null, non_absent, non_default, non_empty
修改配置文件的方式很简单。 缺点是无法自定义一些高级配置,例如为LocalDateTime自定义日期格式。目前我们得到的结果是:
{
"brand": "Lavazza",
"date": "2020-11-16T10:35:34.593"
}
为了实现这一目标,我们将使用自定义日期格式注册一个新的JavaTimeModule:
@Configuration
@PropertySource("classpath:coffee.properties")
public class CoffeeRegisterModuleConfig {
@Bean
public Module javaTimeModule() {
JavaTimeModule module = new JavaTimeModule();
module.addSerializer(LOCAL_DATETIME_SERIALIZER);
return module;
}
}
coffee.properties配置文件中应包含:
spring.jackson.default-property-inclusion=non_null
Spring Boot会自动注册 com.fasterxml.jackson.databind.Module 类型的任何bean。最终结果为
{
"brand": "Lavazza",
"date": "16-11-2020 10:43"
}
3.2.Jackson2ObjectMapperBuilderCustomizer
该函数式接口的目的是允许我们创建configuration Bean。它们将应用于通过Jackson2ObjectMapperBuilder创建的默认ObjectMapper:
@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
return builder -> builder.serializationInclusion(JsonInclude.Include.NON_NULL)
.serializers(LOCAL_DATETIME_SERIALIZER);
}
配置类bean是按特定顺序生效的,我们可以使用 @Order 注解控制顺序。 如果我们要从不同的配置或模块配置 ObjectMapper ,则这种优雅的方法非常适合
4. 覆盖默认配置
如果我们想完全控制配置, 这里有几种选项可以禁用自动配置,并仅允许使用我们自定义的配置。
4.1. ObjectMapper
覆盖默认配置的最简单方法是自定义 ObjectMapper bean并将其标记为 @Primary :
@Bean
@Primary
public ObjectMapper objectMapper() {
JavaTimeModule module = new JavaTimeModule();
module.addSerializer(LOCAL_DATETIME_SERIALIZER);
return new ObjectMapper()
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
.registerModule(module);
}
当我们想完全控制序列化过程,并且我们不想允许通过外部配置时,应该使用这种方法.
4.2. Jackson2ObjectMapperBuilder
另一种简单的方法是定义 Jackson2ObjectMapperBuilder bean 。实际上,Spring Boot在构建 ObjectMapper 时默认情况下使用此构建器,并且会自动选择已定义好的一个。
@Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
return new Jackson2ObjectMapperBuilder().serializers(LOCAL_DATETIME_SERIALIZER)
.serializationInclusion(JsonInclude.Include.NON_NULL);
}
它会默认配置两项:
- Disable MapperFeature.DEFAULT_VIEW_INCLUSION
- Disable DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
根据Jackson2ObjectMapperBuilder 文档中说明, 如果一些模块存在搜索路径中,它们也会自动注册 :
- jackson-datatype-jdk8: support for other Java 8 types like Optional
- jackson-datatype-jsr310: support for Java 8 Date and Time API types
- jackson-datatype-joda: support for Joda-Time types
- jackson-module-kotlin: support for Kotlin classes and data classes
这种方法的优点是Jackson2ObjectMapperBuilder提供了一种简单直观的方法来构建ObjectMapper。.
4.3. MappingJackson2HttpMessageConverter
我们可以定义一个类型为 MappingJackson2HttpMessageConverter 的bean,Spring Boot会自动使用它:
@Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder().serializers(LOCAL_DATETIME_SERIALIZER)
.serializationInclusion(JsonInclude.Include.NON_NULL);
return new MappingJackson2HttpMessageConverter(builder.build());
}
请查看我们的Spring Http Message Converters文章以了解更多信息。
5. 配置测试
为了测试我们的配置是否生效,我们将使用 TestRestTemplate 并将结果序列化为 String 。 这样,我们可以验证 Coffee 对象是否已序列化,返回的结果没有 null 值, 并具有自定义日期格式:
@Test
public void whenGetCoffee_thenSerializedWithDateAndNonNull() {
String formattedDate = DateTimeFormatter.ofPattern(CoffeeConstants.dateTimeFormat).format(FIXED_DATE);
String brand = "Lavazza";
String url = "/coffee?brand=" + brand;
String response = restTemplate.getForObject(url, String.class);
assertThat(response).isEqualTo("{\"brand\":\"" + brand + "\",\"date\":\"" + formattedDate + "\"}");
}
6. 总结
在本教程中,我们研究了使用Spring Boot自定义JSON序列化的几种方法。
和本站其他教程一样,教程源代码存放在GitHub.