Java中如何语义化比较JSON数组?单元测试遇问题求助
解决JSON数组对象键顺序不同但语义相同的单元测试比较问题
我明白你遇到的困扰了——明明两个JSON数组的元素语义完全一致,只是每个对象的键顺序颠倒,用在线工具判定是相同的,但用org.skyscreamer.jsonassert的默认比较模式却始终判定为不同。这是因为默认的LENIENT模式仅忽略数组元素的顺序,并不会忽略对象内部键的顺序,而STRICT模式会同时检查这两者,所以才会出现不符合预期的结果。
下面给你几个实用的解决方案:
方案1:自定义JSONComparator忽略对象键顺序
你可以扩展jsonassert库的比较逻辑,创建一个忽略对象键顺序的自定义比较器:
import org.skyscreamer.jsonassert.*; import org.json.JSONArray; import org.json.JSONObject; public class IgnoreKeyOrderComparator extends DefaultComparator { public IgnoreKeyOrderComparator(JSONCompareMode mode) { super(mode); } @Override public void compareValues(String prefix, Object expected, Object actual, JSONCompareResult result) throws JSONException { if (expected instanceof JSONObject && actual instanceof JSONObject) { JSONObject expectedObj = (JSONObject) expected; JSONObject actualObj = (JSONObject) actual; // 先校验键的集合是否完全匹配 if (!expectedObj.keySet().equals(actualObj.keySet())) { result.fail(prefix, expectedObj, actualObj); return; } // 逐个比较键对应的值,忽略键的顺序 for (String key : expectedObj.keySet()) { String newPrefix = prefix + "." + key; compareValues(newPrefix, expectedObj.get(key), actualObj.get(key), result); } } else { // 非对象类型按默认逻辑比较 super.compareValues(prefix, expected, actual, result); } } }
测试时使用这个自定义比较器:
// 假设Array1和Array2是合法的JSON字符串或JSONArray对象 JSONCompareResult result = JSONCompare.compareJSON(Array1, Array2, new IgnoreKeyOrderComparator(JSONCompareMode.LENIENT)); assertTrue(result.passed()); // 或者用JSONAssert的重载方法 JSONAssert.assertEquals(Array1, Array2, new IgnoreKeyOrderComparator(JSONCompareMode.LENIENT));
方案2:解析为List
另一种更简洁的方式是把JSON数组转换成Java的List<Map<String, Object>>,因为Map本身不关心键的顺序,只要键值对完全匹配即可。这里以Jackson库为例:
import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; import java.util.Map; // 初始化ObjectMapper ObjectMapper mapper = new ObjectMapper(); // 解析JSON数组为List<Map> List<Map<String, Object>> list1 = mapper.readValue(Array1, List.class); List<Map<String, Object>> list2 = mapper.readValue(Array2, List.class); // 直接比较两个列表(如果数组元素顺序无关,可先排序后再比较) assertEquals(list1, list2);
如果用Gson库,代码逻辑类似:
import com.google.gson.Gson; import java.util.List; import java.util.Map; Gson gson = new Gson(); List<Map<String, Object>> list1 = gson.fromJson(Array1, List.class); List<Map<String, Object>> list2 = gson.fromJson(Array2, List.class); assertEquals(list1, list2);
方案3:转换成自定义实体类列表比较(推荐)
如果你的JSON结构固定,最稳妥的方式是定义对应的Java实体类,将JSON转换成实体类列表后再比较。这种方式类型安全,测试逻辑也更清晰:
首先定义实体类:
import java.util.Objects; public class RoleJob { private String job_name; private String role_name; // 生成getter、setter方法 public String getJob_name() { return job_name; } public void setJob_name(String job_name) { this.job_name = job_name; } public String getRole_name() { return role_name; } public void setRole_name(String role_name) { this.role_name = role_name; } // 重写equals和hashCode方法 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; RoleJob roleJob = (RoleJob) o; return Objects.equals(job_name, roleJob.job_name) && Objects.equals(role_name, roleJob.role_name); } @Override public int hashCode() { return Objects.hash(job_name, role_name); } }
然后解析并比较:
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.List; ObjectMapper mapper = new ObjectMapper(); List<RoleJob> roles1 = mapper.readValue(Array1, new TypeReference<List<RoleJob>>() {}); List<RoleJob> roles2 = mapper.readValue(Array2, new TypeReference<List<RoleJob>>() {}); assertEquals(roles1, roles2);
最后提醒一句:你提供的JSON Array2看起来缺少外层的[],这会导致解析失败,记得修正为合法的JSON数组格式哦。
内容的提问来源于stack exchange,提问作者supratim dutta
相关产品推荐
相关产品推荐

