Java中忽略JSON数组顺序比较JSON对象并输出差异的方法
Java中忽略数组顺序的JSON对象对比方案
你当前的扁平化对比方案会因为JSON数组元素顺序不同误判差异——扁平化时会给数组元素加上索引前缀(比如subject/0、subject/1),顺序一变键名就对应不上了。下面给出几种可行的解决思路:
方案一:使用专门的JSON对比库(推荐)
成熟的JSON对比库大多支持忽略数组元素顺序,这里以JSONassert为例,它能直接处理数组顺序问题,还能输出清晰的差异信息:
步骤1:引入Maven依赖
<dependency> <groupId>org.skyscreamer</groupId> <artifactId>jsonassert</artifactId> <version>1.5.1</version> <scope>test</scope> </dependency>
步骤2:编写对比代码
通过CompareMode.LENIENT配置忽略数组顺序,对比失败时会自动输出差异详情:
import org.skyscreamer.jsonassert.JSONAssert; import org.skyscreamer.jsonassert.JSONCompareMode; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; public class JsonCompareUtil { public static void main(String[] args) throws IOException { String baselineJson = Files.readString(Paths.get("input/jsonBaseline/sampleJson1_simple.json")); String regressionJson = Files.readString(Paths.get("input/jsonRegression/sampleJson1_simple.json")); try { // 宽松模式:忽略数组顺序,只对比元素内容 JSONAssert.assertEquals(baselineJson, regressionJson, JSONCompareMode.LENIENT); System.out.println("两个JSON对象逻辑一致,无差异"); } catch (AssertionError e) { System.out.println("JSON差异如下:"); System.out.println(e.getMessage()); } } }
方案二:自定义扁平化逻辑(不依赖第三方库)
如果不想引入外部库,可以修改你的FlatMapUtil,对数组元素按唯一标识排序后再扁平化,比如用subjectId作为排序依据,这样顺序不同的数组会生成相同的扁平化映射:
修改后的FlatMapUtil关键代码
private static Stream<Map.Entry<String, Object>> flatten(Map.Entry<String, Object> entry) { if (entry == null) { return Stream.empty(); } if (entry.getValue() instanceof Map<?, ?>) { return ((Map<?, ?>) entry.getValue()).entrySet().stream() .flatMap(e -> flatten(new AbstractMap.SimpleEntry<>(entry.getKey() + "/" + e.getKey(), e.getValue()))); } if (entry.getValue() instanceof List<?>) { List<?> list = (List<?>) entry.getValue(); Stream<?> sortedStream; // 针对对象数组:按唯一键(比如subjectId)排序 if (list.stream().anyMatch(item -> item instanceof Map<?, ?>)) { sortedStream = list.stream() .map(item -> (Map<?, ?>) item) .sorted((map1, map2) -> { String key1 = map1.get("subjectId").toString(); String key2 = map2.get("subjectId").toString(); return key1.compareTo(key2); }); } else { // 普通类型数组:直接按内容排序 sortedStream = list.stream().sorted(); } // 用元素的唯一标识生成键,避免索引影响 return sortedStream .map(item -> new AbstractMap.SimpleEntry<>(entry.getKey() + "/" + item.hashCode(), item)) .flatMap(FlatMapUtil::flatten); } return Stream.of(entry); }
注意:这种方式需要提前明确数组元素的唯一标识,如果是通用场景,可以用对象的哈希值作为排序依据,但复杂度会更高。
方案三:反序列化为Java对象后对比(适合固定结构JSON)
如果你的JSON结构固定,可以定义对应的Java实体类,重写equals方法忽略数组顺序,直接对比对象:
定义实体类
import java.util.List; import java.util.Objects; public class Student { private String name; private int student_ID; private List<Subject> subject; // getter、setter省略 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return student_ID == student.student_ID && Objects.equals(name, student.name) && // 忽略顺序,检查两个列表元素完全一致 subject.containsAll(student.subject) && student.subject.containsAll(subject); } @Override public int hashCode() { return Objects.hash(name, student_ID); } } class Subject { private int marks; private String subjectId; private String subjectName; // getter、setter省略 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Subject subject = (Subject) o; return marks == subject.marks && Objects.equals(subjectId, subject.subjectId) && Objects.equals(subjectName, subject.subjectName); } @Override public int hashCode() { return Objects.hash(marks, subjectId, subjectName); } }
对比代码
import com.fasterxml.jackson.databind.ObjectMapper; import java.io.File; import java.io.IOException; public class ObjectCompareUtil { public static void main(String[] args) throws IOException { ObjectMapper mapper = new ObjectMapper(); Student baseline = mapper.readValue(new File("input/jsonBaseline/sampleJson1_simple.json"), Student.class); Student regression = mapper.readValue(new File("input/jsonRegression/sampleJson1_simple.json"), Student.class); if (baseline.equals(regression)) { System.out.println("两个JSON逻辑一致"); } else { System.out.println("两个JSON存在差异"); // 可借助Apache Commons Lang的EqualsBuilder进一步定位差异字段 } } }
这种方式逻辑清晰,适合结构固定的JSON场景,也方便扩展自定义对比规则。
内容的提问来源于stack exchange,提问作者Hema
相关产品推荐
相关产品推荐

