2. 1. 概述
JSON是RESTful应用的事实标准。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获取。