2. 1. 概述

JSONRESTful应用的事实标准。Spring使用Jackson库无缝实现对象与JSON的转换。但有时我们需要自定义转换规则,例如忽略空值或空对象。

移除空值能带来性能提升,因为减少了无效数据的传输,同时让API更简洁。本文将探讨如何利用Jackson的映射特性优化REST交互。

2. 2. 空值处理

在请求/响应中,null值通常不包含有效信息。允许null值会增加验证复杂度——当字段存在时需额外检查,而字段缺失时可直接设置默认值。

Jackson提供了便捷的配置方式。使用@JsonInclude(Include.NON_NULL)注解可全局或字段级控制空值排除:

@JsonInclude(Include.NON_NULL)
public class Employee {
    private String lastName;
    private String firstName;
    private long id;
    // 构造器、getter和setter
}

当字段为null时(仅限引用类型),将不会出现在生成的JSON中:

@ParameterizedTest
@MethodSource
void giveEndpointWhenSendEmployeeThanReceiveThatUserBackIgnoringNullValues(Employee expected) throws Exception {
    MvcResult result = sendRequestAndGetResult(expected, USERS);
    String response = result.getResponse().getContentAsString();
    validateJsonFields(expected, response);
}

private void validateJsonFields(Employee expected, String response) throws JsonProcessingException {
    JsonNode jsonNode = mapper.readTree(response);
    Predicate<Field> nullField = s -> isFieldNull(expected, s);
    List<String> nullFields = filterFieldsAndGetNames(expected, nullField);
    List<String> nonNullFields = filterFieldsAndGetNames(expected, nullField.negate());
    nullFieldsShouldBeMissing(nullFields, jsonNode);
    nonNullFieldsShouldNonBeMissing(nonNullFields, jsonNode);
}

⚠️ 该注解对类似null的空值无效,需特殊处理。

2. 3. 缺失值处理

Optional在技术上是非null值,但在JSON中传递无意义的包装对象会污染响应:

{
  "lastName": "John",
  "firstName": "Doe",
  "id": 1,
  "salary": {
    "empty": true,
    "present": false
  }
}

假设员工可选择性公开薪资信息:

@JsonInclude(Include.NON_ABSENT)
public class Employee {
    private String lastName;
    private String firstName;
    private long id;
    private Optional<Salary> salary;
    // 构造器、getter和setter
}

**使用Include.NON_ABSENT可优雅处理空Optional**,避免破坏Optional的设计初衷:

private void validateJsonFields(Employee expected, String response) throws JsonProcessingException {
    JsonNode jsonNode = mapper.readTree(response);
    Predicate<Field> nullField = s -> isFieldNull(expected, s);
    Predicate<Field> absentField = s -> isFieldAbsent(expected, s);
    List<String> nullOrAbsentFields = filterFieldsAndGetNames(expected, nullField.or(absentField));
    List<String> nonNullAndNonAbsentFields = filterFieldsAndGetNames(expected, nullField.negate().and(absentField.negate()));
    nullFieldsShouldBeMissing(nullOrAbsentFields, jsonNode);
    nonNullFieldsShouldNonBeMissing(nonNullAndNonAbsentFields, jsonNode);
}

Include.NON_ABSENT同时处理null和空Optional,一石二鸟。

2. 4. 空值处理

是否在JSON中包含空字符串或空集合?多数情况下应该排除,将其设为null或包装为Optional反而会增加复杂性。

为员工添加姓名拼音和电话号码字段:

@JsonInclude(Include.NON_EMPTY)
public class Employee {
    private String lastName;
    private String firstName;
    private long id;
    private Optional<Salary> salary;
    private String phoneticName = "";
    private List<PhoneNumber> phoneNumbers = new ArrayList<>();
    // 构造器、getter和setter
}

使用Include.NON_EMPTY可排除空值,该注解同时忽略null和缺失值:

private void validateJsonFields(Employee expected, String response) throws JsonProcessingException {
    JsonNode jsonNode = mapper.readTree(response);
    Predicate<Field> nullField = s -> isFieldNull(expected, s);
    Predicate<Field> absentField = s -> isFieldAbsent(expected, s);
    Predicate<Field> emptyField = s -> isFieldEmpty(expected, s);
    List<String> nullOrAbsentOrEmptyFields = filterFieldsAndGetNames(expected, nullField.or(absentField).or(emptyField));
    List<String> nonNullAndNonAbsentAndNonEmptyFields = filterFieldsAndGetNames(expected,
      nullField.negate().and(absentField.negate().and(emptyField.negate())));
    nullFieldsShouldBeMissing(nullOrAbsentOrEmptyFields, jsonNode);
    nonNullFieldsShouldNonBeMissing(nonNullAndNonAbsentAndNonEmptyFields, jsonNode);
}

💡 这些注解可混合使用,甚至能全局配置映射器。

2. 5. 自定义映射器

当默认策略不够灵活时,**可使用Include.CUSTOM或实现自定义序列化器**:

public class CustomEmployeeSerializer extends StdSerializer<Employee> {
    @Override
    public void serialize(Employee employee, JsonGenerator gen, SerializerProvider provider)
      throws IOException {
        gen.writeStartObject();
        // 自定义序列化逻辑
        gen.writeEndObject();
    }
}

2. 6. 总结

Jackson与Spring能以最小配置开发RESTful应用。包含策略可简化API并减少样板代码,当默认方案受限时,可通过自定义映射器或过滤器扩展能力。

本文代码示例可在GitHub获取。


原始标题:Remove Null Objects in JSON Response When Using Spring and Jackson | Baeldung