
本文介绍在单元测试等场景下,如何准确比较两个 json 字符串并自动忽略 null 值字段,确保语义等价性判断更可靠,推荐使用 gson 或 jackson 实现标准化预处理后再比对。
本文介绍在单元测试等场景下,如何准确比较两个 json 字符串并自动忽略 null 值字段,确保语义等价性判断更可靠,推荐使用 gson 或 jackson 实现标准化预处理后再比对。
在 Java 测试中,直接使用字符串或 Object.equals() 比较 JSON 内容极易因格式、空格、字段顺序或冗余 null 值导致误判。例如:
- 期望值:{"field1":"value1"}
- 实际值(含 null):{"field1":"value1","field2":null}
理想情况下应视为相等;但若实际值为 {"field1":"value1","field2":"non-null"},则必须判定为不等。
核心思路是:先标准化(移除所有 null 字段),再进行结构化比对,而非依赖原始字符串匹配。
✅ 推荐方案一:使用 Gson 移除 null 并序列化
Gson 默认在反序列化时保留 null,但通过 JsonObject 中间表示 + 重新序列化,可自然剔除 null 字段(因其 JsonObject 内部不存储 null 键值对):
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
public class JsonNullAwareComparator {
private static final Gson gson = new Gson();
public static boolean equalsIgnoringNulls(String json1, String json2) {
if (json1 == null || json2 == null) {
return json1 == json2;
}
try {
JsonObject obj1 = gson.fromJson(json1, JsonObject.class);
JsonObject obj2 = gson.fromJson(json2, JsonObject.class);
return gson.toJson(obj1).equals(gson.toJson(obj2));
} catch (Exception e) {
throw new IllegalArgumentException("Invalid JSON input", e);
}
}
}✅ 优势:无需额外配置,轻量、稳定、适用于嵌套对象与数组(JsonArray 同理支持)。
⚠️ 注意:该方法会丢失原始 JSON 的键序(Gson 默认不保证 Map 插入顺序),如需严格保序,可配合 LinkedTreeMap 自定义 TypeAdapter,但多数测试场景中字段语义一致性比顺序更重要。
✅ 推荐方案二:使用 Jackson 配置 SerializationFeature.WRITE_NULL_MAP_VALUES = false
Jackson 提供更精细的控制能力,可通过 ObjectMapper 配置全局或临时忽略 null:
立即学习“Java免费学习笔记(深入)”;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
public class JacksonNullAwareComparator {
private static final ObjectMapper mapper = new ObjectMapper()
.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false)
.configure(SerializationFeature.INDENT_OUTPUT, false); // 可选:禁用缩进确保字符串一致
public static boolean equalsIgnoringNulls(String json1, String json2) throws Exception {
JsonNode tree1 = mapper.readTree(json1);
JsonNode tree2 = mapper.readTree(json2);
// 序列化时 null 字段自动跳过
return mapper.writeValueAsString(tree1).equals(mapper.writeValueAsString(tree2));
}
}? 进阶提示:若需深度比对(如忽略数组顺序、浮点精度容差等),可结合 JsonNode.equals()(它本身已忽略 null 字段)——但注意:JsonNode.equals() 不会自动跳过 null!需先调用 tree.traverse() 手动过滤,或统一走序列化路径更稳妥。
? 总结与最佳实践
- ✅ 首选 Gson 方案:简洁、无依赖冲突风险,适合大多数测试断言场景;
- ✅ 选用 Jackson 方案:若项目已重度使用 Jackson,可复用现有配置,且便于集成到 AssertJ 或 JSONAssert 等断言库;
- ❌ 避免正则替换 ":null[,}]":易破坏字符串内 "null" 字面量或嵌套结构,不可靠;
- ✅ 单元测试中建议封装为静态工具方法,并搭配 assertThat(...).isEqualTo(...) 使用,提升可读性与可维护性;
- ? 补充边界测试:空对象 {}、纯 null 字段、深层嵌套 null、null 数组元素等,确保鲁棒性。
通过标准化预处理消除 null 干扰,你将获得真正基于数据语义的 JSON 比较能力——让测试更专注业务逻辑,而非 JSON 序列化的细节差异。










