You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 13:30:40