Spring Boot 接口 JSON 序列化优化:忽略 Null 值的九种解决方案详解

一、针对特定接口null的处理:

方法一:使用 @JsonInclude 注解

1.1 类级别:在接口返回的 ‌DTO 类或字段‌ 上添加 @JsonInclude 注解,强制忽略 null 值:

类级别:所有字段为 null 时不返回

java 复制代码
@JsonInclude(JsonInclude.Include.NON_NULL)
public class MyResponseDTO {
    private String field1;
    

1.2 字段级别:在具体字段上,仅该字段为 null 时不返回

java 复制代码
// 字段级别:仅该字段为 null 时不返回
@JsonInclude(JsonInclude.Include.NON_NULL)
private String field2;

1.3 方法级别: 在方法级别使用 @JsonInclude

如果不想在 DTO 类上全局标注 @JsonInclude,可以直接在 Controller 方法的返回类型上通过 @JsonSerialize 注解临时指定序列化行为。

java 复制代码
@GetMapping("/user")
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL) // 旧版 Jackson 注解
public MyResponse getUser() {
    MyResponse response = new MyResponse();
    response.setName("Alice"); // age 为 null,不返回
    return response;
}

方法二:动态构建响应对象

在 Controller 方法中手动过滤 null 值,使用 Map 或 JSONObject 动态构建响应体:

java 复制代码
@GetMapping("/api")
public Map<String, Object> getData() {
    MyResponseDTO dto = service.getData();
    Map<String, Object> result = new HashMap<>();
    if (dto.getField1() != null) {
        result.put("field1", dto.getField1());
    }
    return result;
}

适用于简单场景,但需手动维护字段映射‌。

方法三:自定义序列化逻辑(针对复杂场景)

通过继承 JsonSerializer 实现特定字段的 null 处理逻辑,并在 DTO 中指定序列化器:

java 复制代码
public class NullSerializer extends JsonSerializer<Object> {
    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) {
        // 忽略 null 字段
        if (value == null) {
            return;
        }
        gen.writeObject(value);
    }
}

// 在 DTO 字段上指定序列化器
public class MyResponseDTO {
    @JsonSerialize(using = NullSerializer.class)
    private String field1;
}

适用于需要精细化控制序列化逻辑的场景‌。

方法四:自定义 ObjectMapper 并局部使用

通过注入 ObjectMapper 实例,在特定接口中手动序列化数据,跳过 null 值。

java 复制代码
@Autowired
private ObjectMapper objectMapper;

@GetMapping("/user")
public String getUser(HttpServletResponse response) throws JsonProcessingException {
    MyResponse data = new MyResponse();
    data.setName("Alice"); // age 为 null

    // 临时配置 ObjectMapper 忽略 null
    objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    String json = objectMapper.writeValueAsString(data);
    
    // 恢复全局配置(可选)
    objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS);
    
    response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    return json;
}

注意 :此方法需手动处理响应,适用于需要完全控制序列化逻辑的场景,但需谨慎管理 ObjectMapper 的线程安全性。

方法五:使用 ResponseBodyAdvice 全局拦截并处理

通过实现 ResponseBodyAdvice 接口,对特定接口或全局返回值进行统一处理。

  1. 定义切面类

    java 复制代码
    @RestControllerAdvice
    public class NullIgnoreAdvice implements ResponseBodyAdvice<Object> {
    
        @Override
        public boolean supports(MethodParameter returnType, Class converterType) {
            // 仅处理特定接口(根据注解、包路径等条件判断)
            return returnType.getExecutable().getName().equals("getUser");
        }
    
        @Override
        public Object beforeBodyWrite(Object body, MethodParameter returnType, 
                                     MediaType mediaType, Class selectedConverterType,
                                     ServerHttpRequest request, ServerHttpResponse response) {
            if (body instanceof MyResponse) {
                // 手动移除 null 值(需根据数据结构处理)
                ((MyResponse) body).setAge(null); // 示例
            }
            return body;
        }
    }
  2. 结合 ObjectMapper 动态过滤

    在 beforeBodyWrite 中重新序列化数据:

    java 复制代码
    ObjectMapper mapper = new ObjectMapper();
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    return mapper.convertValue(body, returnType.getParameterType());

二、针对所有接口null 的处理:

2.1 :全局配置(影响所有接口)

若需所有接口忽略null值,可在application.properties中配置:

java 复制代码
spring.jackson.default-property-inclusion=non_null

2.2 Springboot 整合 fastjson:

在springboot 启动类中定义该方法即可

java 复制代码
  @Bean
  public HttpMessageConverters fastJsonHttpMessageConverters() {
    FastJsonHttpMessageConverter4 fastConverter = new FastJsonHttpMessageConverter4();
    FastJsonConfig fastJsonConfig = new FastJsonConfig();
    fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat, SerializerFeature.IgnoreNonFieldGetter,
        SerializerFeature.WriteMapNullValue, SerializerFeature.WriteNullStringAsEmpty);
    fastConverter.setFastJsonConfig(fastJsonConfig);
    List supportedMediaTypes = new ArrayList();
    supportedMediaTypes.add(new MediaType("text", "json", Charset.forName("utf8")));
    supportedMediaTypes.add(new MediaType("application", "json", Charset.forName("utf8")));
    fastConverter.setSupportedMediaTypes(supportedMediaTypes);
    HttpMessageConverter<?> converter = fastConverter;
    return new HttpMessageConverters(converter);
  }

2.3 Springboot 整合 jackson:

java 复制代码
@Configuration
public class JacksonConfig {
  @Bean
  @Primary
  @ConditionalOnMissingBean(ObjectMapper.class)
  public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
    ObjectMapper objectMapper = builder.createXmlMapper(false).build();
    objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
      @Override
      public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString("");
      }
    });
    return objectMapper;
  }
}

三、总结:

  1. 针对新项目,推荐使用全局序列化处理,统一返回值。
  2. 针对老项目,推荐使用特定接口的处理,避免影响了其他接口的稳定。
相关推荐
曲径海盗8 小时前
【Python 学习第 40 天】for 循环
python·学习笔记·编程入门
专注仿真8 小时前
知识问答大模型-rag算法详解
人工智能·python·控制算法
染指11108 小时前
158.Agent-LangGraph-子图(共享状态和不共享状态模式)
开发语言·人工智能·python·ui·agent·agents
云和数据.ChenGuang10 小时前
Python 常见特殊方法详解:__new__、__init__、__del__、__str__、__repr__ 与 __getattribute__
开发语言·人工智能·python·1024程序员节
xyouyuweizhu10 小时前
字节流与字符流:Java IO 的两条主线该怎么选
java·开发语言·python
迪菲赫尔曼10 小时前
【基础篇 04】第一次使用终端:命令在哪里输入,电脑现在又“站”在哪里?
python·yolo·目标检测
qianbo_insist11 小时前
# 在 Qwen3.5-2B 上把 GDN-1 无损换成 GDN-2:一次完整的架构手术实验(负结果)
人工智能·pytorch·python·架构
专注仿真12 小时前
Agentic RAG算法详解
人工智能·python·大模型训练·控制算法
学习论之费曼学习法12 小时前
无人值守AI内容矩阵实战(8):标题 A/B 实验的 6 个工程实现细节,隔日对比与自动判胜
人工智能·python·算法·矩阵·自动化·a/b测试·内容矩阵
东哥168812 小时前
我用 DeepSeek 做了 4 个零依赖的赚钱小工具,全部开源
python·开源·deepseek