使用net.minidev.json比较复杂JSON对象时如何忽略指定深层字段?
如何忽略复杂JSON对象中的特定深层字段进行单元测试比较?
你使用net.minidev.json库编写单元测试时,需要比较两个复杂JSONObject但忽略某个始终可变的深层字段(如timestamp),直接用Assertions.assertEquals会因为该字段值不同导致测试失败。下面提供两种简单可行的方案,无需编写复杂的自定义比较器:
方案一:递归删除目标深层字段
通过编写工具方法,递归遍历JSON结构,找到指定路径的字段并删除,之后再进行比较。这种方式能确保两个JSON对象在忽略目标字段后结构和值完全一致。
工具类代码
import net.minidev.json.JSONArray; import net.minidev.json.JSONObject; public class JsonTestUtils { // 递归删除指定路径的深层字段 public static void removeDeepField(Object json, String[] fieldPath) { if (fieldPath == null || fieldPath.length == 0) { return; } Object currentNode = json; // 遍历到目标字段的父节点 for (int i = 0; i < fieldPath.length - 1; i++) { String key = fieldPath[i]; if (currentNode instanceof JSONObject) { currentNode = ((JSONObject) currentNode).get(key); } else if (currentNode instanceof JSONArray) { // 支持路径中包含数组索引(需传入数字字符串,如"0") try { int index = Integer.parseInt(key); currentNode = ((JSONArray) currentNode).get(index); } catch (NumberFormatException e) { // 路径格式错误,直接终止 return; } } else { // 中间路径不存在,终止操作 return; } if (currentNode == null) { return; } } // 删除最后一级的目标字段 String finalKey = fieldPath[fieldPath.length - 1]; if (currentNode instanceof JSONObject) { ((JSONObject) currentNode).remove(finalKey); } else if (currentNode instanceof JSONArray) { try { int index = Integer.parseInt(finalKey); ((JSONArray) currentNode).remove(index); } catch (NumberFormatException e) { return; } } } }
测试代码改造
JSONParser parser = new JSONParser(JSONParser.MODE_JSON_SIMPLE); JSONObject jsonObject1 = (JSONObject) parser.parse(expectedErrorMessage); JSONObject jsonObject2 = (JSONObject) parser.parse(lastMessage.toString()); // 指定要忽略的深层字段路径,例如["data","metadata","timestamp"] String[] ignoredFieldPath = {"path", "to", "field"}; JsonTestUtils.removeDeepField(jsonObject1, ignoredFieldPath); JsonTestUtils.removeDeepField(jsonObject2, ignoredFieldPath); // 此时比较的是忽略目标字段后的JSON对象 Assertions.assertEquals(jsonObject1, jsonObject2);
方案二:统一设置目标字段为固定值
如果不想删除字段,也可以将两个JSON对象的目标字段设置为同一个固定值,确保比较时该字段不会影响结果,逻辑和方案一类似,只是将删除操作改为赋值操作。
工具类扩展方法
// 递归设置指定路径的深层字段为固定值 public static void setDeepField(Object json, String[] fieldPath, Object fixedValue) { if (fieldPath == null || fieldPath.length == 0) { return; } Object currentNode = json; // 遍历到目标字段的父节点 for (int i = 0; i < fieldPath.length - 1; i++) { String key = fieldPath[i]; if (currentNode instanceof JSONObject) { currentNode = ((JSONObject) currentNode).get(key); } else if (currentNode instanceof JSONArray) { try { int index = Integer.parseInt(key); currentNode = ((JSONArray) currentNode).get(index); } catch (NumberFormatException e) { return; } } else { return; } if (currentNode == null) { return; } } // 设置最后一级的目标字段值 String finalKey = fieldPath[fieldPath.length - 1]; if (currentNode instanceof JSONObject) { ((JSONObject) currentNode).put(finalKey, fixedValue); } else if (currentNode instanceof JSONArray) { try { int index = Integer.parseInt(finalKey); ((JSONArray) currentNode).set(index, fixedValue); } catch (NumberFormatException e) { return; } } }
测试代码改造
JSONParser parser = new JSONParser(JSONParser.MODE_JSON_SIMPLE); JSONObject jsonObject1 = (JSONObject) parser.parse(expectedErrorMessage); JSONObject jsonObject2 = (JSONObject) parser.parse(lastMessage.toString()); String[] targetFieldPath = {"path", "to", "field"}; // 将两个对象的目标字段都设为同一个固定值 JsonTestUtils.setDeepField(jsonObject1, targetFieldPath, "ignored_timestamp"); JsonTestUtils.setDeepField(jsonObject2, targetFieldPath, "ignored_timestamp"); Assertions.assertEquals(jsonObject1, jsonObject2);
方案对比
- 方案一更彻底,直接移除无关字段,避免后续其他字段修改带来的干扰;
- 方案二保留字段结构,适合需要维持JSON完整性的场景;
- 两种方案都比自定义比较器更简洁,无需重写复杂的递归比较逻辑,复用性强。
内容的提问来源于stack exchange,提问作者Oliver Watkins
相关产品推荐
相关产品推荐

