Spring Boot: Customize the Jackson ObjectMapper – Spring Boot 自定义Jackson ObjectMapper

最后修改: 2020年 12月 22日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

When using JSON format, Spring Boot will use an ObjectMapper instance to serialize responses and deserialize requests.

当使用JSON格式时,Spring Boot将使用一个ObjectMapper实例来序列化响应和反序列化请求。

In this tutorial, we’ll take a look at the most common ways to configure the serialization and deserialization options.

在本教程中,我们将看看配置序列化和反序列化选项的最常用方法。

To learn more about Jackson, be sure to check out our Jackson tutorial.

要了解更多关于杰克逊的信息,请务必查看我们的杰克逊教程

2. Default Configuration

2.默认配置

By default, the Spring Boot configuration will disable the following:

默认情况下,Spring Boot配置将禁用以下内容。

  • MapperFeature.DEFAULT_VIEW_INCLUSION
  • DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
  • SerializationFeature.WRITE_DATES_AS_TIMESTAMPS

Let’s start with a quick example:

让我们从一个简单的例子开始。

  • The client will send a GET request to our /coffee?name=Lavazza.
  • The controller will return a new Coffee object.
  • Spring will use ObjectMapper to serialize our POJO to JSON.

We’ll exemplify the customization options by using String and LocalDateTime objects:

我们将通过使用StringLocalDateTime对象来示范定制选项。

public class Coffee {

    private String name;
    private String brand;
    private LocalDateTime date;

   //getters and setters
}

We’ll also define a simple REST controller to demonstrate the serialization:

我们还将定义一个简单的REST控制器来演示序列化

@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);
}

By default, this will be the response when calling GET http://lolcahost:8080/coffee?brand=Lavazza:

默认情况下,这将是调用GET http://lolcahost:8080/coffee?brand=Lavazza时的响应。

{
  "name": null,
  "brand": "Lavazza",
  "date": "2020-11-16T10:21:35.974"
}

We would like to exclude null values and to have a custom date format (dd-MM-yyyy HH:mm). This is our final response:

我们希望排除null值,并希望有一个自定义的日期格式(dd-MM-yyyy HH:mm)。这就是我们的最终答复。

{
  "brand": "Lavazza",
  "date": "04-11-2020 10:34"
}

When using Spring Boot, we have the option to customize the default ObjectMapper or to override it. We’ll cover both options in the next sections.

在使用Spring Boot时,我们可以选择定制默认的ObjectMapper或覆盖它。我们将在接下来的章节中介绍这两个选项。

3. Customizing the Default ObjectMapper

3.定制默认的ObjectMapper

In this section, we’ll see how to customize the default ObjectMapper that Spring Boot uses.

在本节中,我们将看到如何定制Spring Boot使用的默认ObjectMapper

3.1. Application Properties and Custom Jackson Module

3.1.应用程序属性和自定义Jackson模块

The simplest way to configure the mapper is via application properties.

配置映射器的最简单方法是通过应用程序属性。

Here’s the general structure of the configuration:

下面是配置的一般结构。

spring.jackson.<category_name>.<feature_name>=true,false

As an example, here’s what we’ll add to disable SerializationFeature.WRITE_DATES_AS_TIMESTAMPS:

作为一个例子,下面是我们要添加的内容,以禁用SerializationFeature.WRITE_DATES_AS_TIMESTAMPS

spring.jackson.serialization.write-dates-as-timestamps=false

Besides the mentioned feature categories, we can also configure property inclusion:

除了提到的特征类别,我们还可以配置属性包含。

spring.jackson.default-property-inclusion=always, non_null, non_absent, non_default, non_empty

Configuring the environment variables is the simplest approach. The downside of this approach is that we can’t customize advanced options like having a custom date format for LocalDateTime.

配置环境变量是最简单的方法。这种方法的缺点是,我们无法定制高级选项,例如为LocalDateTime定制日期格式。

At this point, we’ll obtain this result:

在这一点上,我们会得到这个结果。

{
  "brand": "Lavazza",
  "date": "2020-11-16T10:35:34.593"
}

In order to achieve our goal, we’ll register a new JavaTimeModule with our custom date format:

为了实现我们的目标,我们将注册一个新的JavaTimeModule与我们的自定义日期格式。

@Configuration
@PropertySource("classpath:coffee.properties")
public class CoffeeRegisterModuleConfig {

    @Bean
    public Module javaTimeModule() {
        JavaTimeModule module = new JavaTimeModule();
        module.addSerializer(LOCAL_DATETIME_SERIALIZER);
        return module;
    }
}

Also, the configuration properties file coffee.properties will contain the following:

另外,配置属性文件coffee.properties将包含以下内容。

spring.jackson.default-property-inclusion=non_null

Spring Boot will automatically register any bean of type com.fasterxml.jackson.databind.Module. Here’s our final result:

Spring Boot将自动注册任何com.fasterxml.jackson.databind.Module类型的bean。下面是我们的最终结果。

{
  "brand": "Lavazza",
  "date": "16-11-2020 10:43"
}

3.2. Jackson2ObjectMapperBuilderCustomizer

3.2.Jackson2ObjectMapperBuilderCustomizer

The purpose of this functional interface is to allow us to create configuration beans.

这个功能接口的目的是让我们创建配置Bean。

They will be applied to the default ObjectMapper created via Jackson2ObjectMapperBuilder:

它们将被应用到通过Jackson2ObjectMapperBuilder创建的默认ObjectMapper

@Bean
public Jackson2ObjectMapperBuilderCustomizer jsonCustomizer() {
    return builder -> builder.serializationInclusion(JsonInclude.Include.NON_NULL)
      .serializers(LOCAL_DATETIME_SERIALIZER);
}

The configuration beans are applied in a specific order, which we can control using the @Order annotation. This elegant approach is suitable if we want to configure the ObjectMapper from different configurations or modules.

配置Bean以特定的顺序应用,我们可以使用@Order annotation来控制。如果我们想从不同的配置或模块来配置ObjectMapper,这种优雅的方法是适合的。

4. Overriding the Default Configuration

4.覆盖默认配置

If we want to have full control over the configuration, there are several options that will disable the auto-configuration and allow only our custom configuration to be applied.

如果我们想完全控制配置,有几个选项将禁用自动配置,只允许应用我们的自定义配置。

Let’s take a close look at these options.

让我们仔细看一下这些选项。

4.1. ObjectMapper

4.1.ObjectMapper

The simplest way to override the default configuration is to define an ObjectMapper bean and to mark it as @Primary:

覆盖默认配置的最简单方法是定义一个 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);
}

We should use this approach when we want to have full control over the serialization process and don’t want to allow external configuration.

当我们希望完全控制序列化过程,并且不希望允许外部配置时,我们应该使用这种方法。

4.2. Jackson2ObjectMapperBuilder

4.2.Jackson2ObjectMapperBuilder

Another clean approach is to define a Jackson2ObjectMapperBuilder bean.

另一个干净的方法是定义一个Jackson2ObjectMapperBuilder bean

Spring Boot actually uses this builder by default when building the ObjectMapper and will automatically pick up the defined one:

实际上,Spring Boot在构建ObjectMapper时默认使用这个构建器,并将自动拾取已定义的构建器。

@Bean
public Jackson2ObjectMapperBuilder jackson2ObjectMapperBuilder() {
    return new Jackson2ObjectMapperBuilder().serializers(LOCAL_DATETIME_SERIALIZER)
      .serializationInclusion(JsonInclude.Include.NON_NULL);
}

It will configure two options by default:

它将默认配置两个选项。

  • disable MapperFeature.DEFAULT_VIEW_INCLUSION
  • disable DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES

According to the Jackson2ObjectMapperBuilder documentation, it will also register some modules if they’re present on the classpath:

根据Jackson2ObjectMapperBuilder文档,如果classpath上有一些模块,它也将注册这些模块。

  • 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

The advantage of this approach is that the Jackson2ObjectMapperBuilder offers a simple and intuitive way to build an ObjectMapper.

这种方法的优点是,Jackson2ObjectMapperBuilder提供了一种简单而直观的方法来构建ObjectMapper

4.3. MappingJackson2HttpMessageConverter

4.3.MappingJackson2HttpMessageConverter

We can just define a bean with type MappingJackson2HttpMessageConverter, and Spring Boot will automatically use it:

我们只需定义一个类型为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());
}

Be sure to check out our Spring Http Message Converters article to learn more.

请务必查看我们的Spring Http消息转换器文章以了解更多。

5. Testing the Configuration

5.测试配置

To test our configuration, we’ll use TestRestTemplate and serialize the objects as String.

为了测试我们的配置,我们将使用TestRestTemplate并将对象序列化为String

In this way, we can validate that our Coffee object is serialized without null values and with the custom date format:

通过这种方式,我们可以验证我们的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. Conclusion

6.结语

In this article, we took a look at several methods to configure the JSON serialization options when using Spring Boot.

在这篇文章中,我们看了一下使用Spring Boot时配置JSON序列化选项的几种方法。

We saw two different approaches: configuring the default options or overriding the default configuration.

我们看到两种不同的方法:配置默认选项或覆盖默认配置。

As always, the full source code of the article is available over on GitHub.

一如既往,文章的完整源代码可在GitHub上获得