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

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

方法一:使用 @JsonInclude 注解

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

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

@JsonInclude(JsonInclude.Include.NON_NULL)
public class MyResponseDTO {
    private String field1;
    

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

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

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

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

@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 动态构建响应体:

@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 中指定序列化器:

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 值。

@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. 定义切面类

    @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 中重新序列化数据:

    ObjectMapper mapper = new ObjectMapper();
    mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
    return mapper.convertValue(body, returnType.getParameterType());
    

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

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

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

spring.jackson.default-property-inclusion=non_null

2.2 Springboot 整合 fastjson:

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

  @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:

@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. 针对老项目,推荐使用特定接口的处理,避免影响了其他接口的稳定。
<think>嗯,用户现在想了解Spring Boot序列化。首先,我需要确认他们对序列化的基本概念是否清楚。序列化就是把对象转换成字节流或其他格式,以便存储或传输,反序列化则是相反的过程。Spring Boot中常用的是JSON序列化,可能用到Jackson或者Gson库。 接下来,用户可能想知道在Spring Boot中如何配置和自定义序列化。比如,如何控制日期格式、处理空、或者自定义字段名称。这时候需要提到Jackson的注解,比如@JsonFormat、@JsonInclude,以及如何通过配置类修改ObjectMapper的设置。 另外,用户可能遇到实际问题,比如循环引用导致序列化失败,或者需要忽略某些字段。这时候需要解释@JsonIgnore、@JsonManagedReference和@JsonBackReference的用法,以及如何配置ObjectMapper来忽略null。 可能用户还关心性能方面的优化,比如使用哪个库更高效,或者如何自定义序列化器。这时候需要比较Jackson和Gson的特点,说明如何通过实现JsonSerializer来自定义处理逻辑。 此外,用户可能不清楚Spring Boot默认使用的序列化库是什么,以及如何切换。需要指出Spring Boot默认使用Jackson,并给出切换为Gson的配置方法,比如在pom.xml中排除Jackson并引入Gson依赖,然后在配置文件中设置。 还要考虑到实际开发中的常见问题,比如LocalDateTime的格式处理,或者多态类型的序列化。这时候需要提供具体的代码示例,比如在实体类中使用@JsonFormat注解,或者配置ObjectMapper注册模块来处理Java 8的日期类型。 最后,总结一下Spring Boot序列化的关键点,强调配置灵活性和常用注解的使用,帮助用户系统理解并应用到实际项目中。</think>### Spring Boot 序列化 **系统详解** --- #### 一、序列化的核心概念 1. **什么是序列化?** - **序列化**:将对象转换为可存储或传输的格式(如 JSON、XML、二进制流)。 - **反序列化**:将序列化后的数据还原为对象。 - **应用场景**:HTTP API 响应、缓存存储、消息队列传输等。 2. **Spring Boot 中的默认序列化工具** - **Jackson**:Spring Boot 默认集成(通过 `spring-boot-starter-web`),支持 JSON 序列化与反序列化。 - 其他选项:Gson、Fastjson(需手动配置)。 --- #### 二、Jackson 的 **核心配置** 1. **全局配置(`application.properties`)** ```properties # 日期格式 spring.jackson.date-format=yyyy-MM-dd HH:mm:ss spring.jackson.time-zone=GMT+8 # 空处理 spring.jackson.default-property-inclusion=non_null # 忽略 null # 美化输出 spring.jackson.serialization.indent-output=true ``` 2. **自定义 `ObjectMapper`** ```java @Configuration public class JacksonConfig { @Bean public ObjectMapper objectMapper() { ObjectMapper mapper = new ObjectMapper(); mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd")); mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); // 忽略和空字符串 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // 忽略未知字段 return mapper; } } ``` --- #### 三、常用 **注解** 1. **字段控制** | 注解 | 作用 | |---------------------|----------------------------------------| | `@JsonIgnore` | 忽略字段序列化和反序列化均不处理) | | `@JsonProperty` | 自定义 JSON 字段名(如 `@JsonProperty("user_name")`) | | `@JsonInclude` | 指定字段包含条件(如 `Include.NON_NULL`) | 2. **时间格式处理** ```java public class User { @JsonFormat(pattern = "yyyy/MM/dd", timezone = "Asia/Shanghai") private LocalDateTime createTime; } ``` 3. **多态类型处理** ```java @JsonTypeInfo(use = Id.NAME, include = As.PROPERTY, property = "type") @JsonSubTypes({ @JsonSubTypes.Type(value = Dog.class, name = "dog"), @JsonSubTypes.Type(value = Cat.class, name = "cat") }) public abstract class Animal { /* ... */ } ``` --- #### 四、**自定义序列化器** 1. **实现 `JsonSerializer`** ```java public class CustomDateSerializer extends JsonSerializer<LocalDateTime> { @Override public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeString(value.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME)); } } // 在字段上应用 public class Order { @JsonSerialize(using = CustomDateSerializer.class) private LocalDateTime orderTime; } ``` 2. **注册自定义模块** ```java @Bean public Module customModule() { SimpleModule module = new SimpleModule(); module.addSerializer(LocalDateTime.class, new CustomDateSerializer()); return module; } ``` --- #### 五、**处理循环引用** 1. **问题场景** ```java @Entity public class User { @OneToMany(mappedBy = "user") private List<Order> orders; } @Entity public class Order { @ManyToOne @JoinColumn(name = "user_id") private User user; } ``` - 序列化 `User` 时会递归序列化 `Order`,导致堆栈溢出。 2. **解决方案** - **使用 `@JsonIgnore`**: ```java public class User { @JsonIgnore // 忽略 orders 字段序列化 private List<Order> orders; } ``` - **使用 `@JsonManagedReference` 和 `@JsonBackReference`**: ```java public class User { @JsonManagedReference // 标记“主控方” private List<Order> orders; } public class Order { @JsonBackReference // 标记“被控方” private User user; } ``` --- #### 六、**切换序列化工具(如 Gson)** 1. **排除 Jackson,引入 Gson** ```xml <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> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency> ``` 2. **配置 Gson** ```java @Bean public Gson gson() { return new GsonBuilder() .setDateFormat("yyyy-MM-dd") .serializeNulls() // 序列化 null .create(); } ``` --- #### 七、**常见问题与调试** 1. **日期格式不一致** - **现象**:返回的日期为长整型时间戳。 - **解决**:使用 `@JsonFormat` 注解或配置全局日期格式。 2. **字段丢失或命名不符合预期** - **检查点**:确认是否使用 `@JsonProperty` 或 Lombok 的 `@Getter`/`@Setter`。 3. **序列化性能优化** - 使用 `Jackson` 的 `Afterburner` 模块加速: ```xml <dependency> <groupId>com.fasterxml.jackson.module</groupId> <artifactId>jackson-module-afterburner</artifactId> </dependency> ``` --- ### 总结 - **默认使用 Jackson**:通过注解和配置可灵活控制序列化行为。 - **关键注解**:`@JsonFormat`、`@JsonIgnore`、`@JsonProperty`。 - **避免循环引用**:使用 `@JsonManagedReference` 和 `@JsonBackReference`。 - **自定义需求**:通过实现 `JsonSerializer` 或切换序列化工具(如 Gson)实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值