在 Spring Boot 中使用 Gson 替换 Jackson

GSON 是由谷歌开源的一款 Java JSON 库。特点是轻量,只有一个 JAR,无任何其他依赖。高性能,支持以流式进行序列化/反序列化。并且抽象了基本的 JsonElementJsonObjectJsonArrayJsonPrimitiveJsonNull,可以在无自定义 Java 对象的情况下构建、解析 JSON 对象。

本文将会指导你如何在 Spring Boot 中使用 Gson 代替默认的 Jackson 作为 JSON 序列化/反序列化框架。

添加依赖

pom.xml 中添加依赖。 gson 的版本已经被 Spring Boot 管理,所以不需要声明版本号。

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
</dependency>

由于 Spring Boot 默认使用 Jackson,所以很多 Spring Boot 的组件、框架默认也会选择用 Jackson 作为 JSON 库。如果你确认项目中,没有其他地方使用到了 Jackson,那么可以从依赖中排除它。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-json</artifactId>
        </exclusion>
    </exclusions>
</dependency>

并且,在 @SpringBootApplication 注解中,排除掉 Jackson 的自动装配。

@SpringBootApplication(exclude = { JacksonAutoConfiguration.class })

配置属性

application.properties 中添加如下配置,指定 Spirng MVC 使用 Gson 的 HttpMessageConverter 实现,GsonHttpMessageConverter

spring.mvc.converters.preferred-json-mapper=gson

Spring Boot 为 Gson 预定义了很多配置属性,你可以通过它们来自定义 Gson 的特性:

# Date 对象序列化时使用的格式。
spring.gson.date-format=
# 是否禁止转义 HTML 字符,如 "<"、">" 等。
spring.gson.disable-html-escaping=
# 序列化时是否排除内部类。
spring.gson.disable-inner-class-serialization=
# 是否启用复杂 map key(即非基础类型)的序列化。
spring.gson.enable-complex-map-key-serialization=
# 是否将所有没有 @Expose 注解的字段排除在序列化或反序列化之外。
spring.gson.exclude-fields-without-expose-annotation=
# 在序列化和反序列化时应用于对象字段的命名策略。
spring.gson.field-naming-policy=
# 是否通过在输出前添加一些特殊文本来生成不可执行的 JSON。
spring.gson.generate-non-executable-json=
# 是否对不符合 RFC 4627 的 JSON 进行宽松解析。
spring.gson.lenient=
# Long 和 long 类型的序列化策略。
spring.gson.long-serialization-policy=
# 是否输出格式化后的 JSON 字符串。
spring.gson.pretty-printing=
# 是否序列化 null 字段。
spring.gson.serialize-nulls=

具体的属性说明,请参考 官方文档

配置类

如果配置属性不满足需求,那么也可以通过在配置类中自定义 GsonBuilder Bean 来对 Gson 进行自定义配置。

例如,自定义 Gson 对 LocalDateTime 类型的格式化:

package cn.springdoc.demo.config;

import java.lang.reflect.Type;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

import org.springframework.boot.autoconfigure.gson.GsonBuilderCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonPrimitive;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;

@Configuration
public class GsonConfiguration {

    @Bean
    public GsonBuilder gsonBuilder(List<GsonBuilderCustomizer> customizers) { // customizers 是读取到的配置文件中关于 Gson 的配置

        // 创建 GsonBuilder
        GsonBuilder builder = new GsonBuilder();
        
        // 加载配置文件中的配置属性
        customizers.forEach((c) -> c.customize(builder));
        
        /**
         * 编程式自定义
         */
        DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        
        // LocalDateTime 类型的格式化
        builder.registerTypeHierarchyAdapter(LocalDateTime.class, new JsonSerializer<LocalDateTime>() {
            @Override
            public JsonElement serialize(LocalDateTime src, Type typeOfSrc, JsonSerializationContext context) {
                return new JsonPrimitive(DATE_TIME_FORMATTER.format(src));
            }
        });
        
        return builder;
    }
}

测试

整合成功后,你可以在任意地方注入 Gson 对象使用。

创建测试类,进行测试:

import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.Map;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.test.context.junit4.SpringRunner;

import com.google.gson.Gson;

import cn.springdoc.demo.DemoApplication;

@RunWith(SpringRunner.class)
@SpringBootTest(classes = DemoApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT)
public class DemoApplicationTest {

    static final Logger logger = LoggerFactory.getLogger(DemoApplicationTest.class);

    @Autowired
    private Gson gson;  // 注入 Gson

    @Test
    public void test() {
        
        Map<String, Object> map = new LinkedHashMap<>();
        
        map.put("title", "spring 中文网");
        map.put("url", "https://springdoc.cn/");
        map.put("creatAt", LocalDateTime.now());
        
        String json = this.gson.toJson(map);
        
        logger.info(json);
    }
}

输出日志如下:

INFO 13020 --- [           main] cn.springdoc.test.DemoApplicationTest    : {"title":"spring 中文网","url":"https://springdoc.cn/","creatAt":"2023-09-19 09:21:23"}

如你所见,注入成功,配置生效,一切OK。