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. 针对老项目,推荐使用特定接口的处理,避免影响了其他接口的稳定。
相关推荐
一 乐1 小时前
点餐|智能点餐系统|基于java+ Springboot的动端的点餐系统小程序(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·小程序·论文
天天进步20152 小时前
Python全栈项目:结合Puppeteer和AI模型操作浏览器
开发语言·人工智能·python
闲人编程2 小时前
用Python识别图片中的文字(Tesseract OCR)
开发语言·python·ocr·识图·codecapsule
盘古开天16663 小时前
从零开始:如何搭建你的第一个简单的Flask网站
后端·python·flask
二进制星轨3 小时前
Transofrmer架构详解与PyTorch实现(附代码讲解)
人工智能·pytorch·python
生而为虫4 小时前
02.第一个Python程序
开发语言·python
视觉AI4 小时前
如何查看 Linux 下正在运行的 Python 程序是哪一个
linux·人工智能·python
程序定小飞5 小时前
基于springboot的作业管理系统设计与实现
java·开发语言·spring boot·后端·spring
猫头虎5 小时前
永久免费白嫖多个域名,一键托管Cloudflare,免费申请SSL加密证书,轻松建站、搭建线路伪装
服务器·开发语言·网络·数据库·python·网络协议·ssl
沙虫一号5 小时前
线上python问题排查思路
后端·python