1. 概述

在这个教程中,我们将探讨如何使用Jackson——一个针对Java的JSON处理库,来比较两个JSON对象。

2. Maven依赖

首先,让我们添加jackson-databind的Maven依赖:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.3</version>
</dependency>

3. 使用Jackson比较两个JSON对象

我们将使用ObjectMapper类将对象读取为JsonNode

让我们创建一个ObjectMapper

ObjectMapper mapper = new ObjectMapper();

3.1. 比较两个简单的JSON对象

我们首先使用JsonNode.equals()方法。equals()方法执行全面(深度)比较。

假设我们有一个定义为s1变量的JSON字符串:

{
    "employee":
    {
        "id": "1212",
        "fullName": "John Miles",
        "age": 34
    }
}

我们要将其与另一个JSON,s2进行比较:

{   
    "employee":
    {
        "id": "1212",
        "age": 34,
        "fullName": "John Miles"
    }
}

让我们将输入JSON作为JsonNode并进行比较:

assertEquals(mapper.readTree(s1), mapper.readTree(s2));

需要注意的是,尽管输入JSON变量s1s2中的属性顺序不同,但equals()方法会忽略顺序,视它们相等。

3.2. 比较包含嵌套元素的JSON对象

接下来,我们将看到如何比较具有嵌套元素的两个JSON对象。

我们从一个定义为s1的JSON开始:

{ 
    "employee":
    {
        "id": "1212",
        "fullName":"John Miles",
        "age": 34,
        "contact":
        {
            "email": "[email protected]",
            "phone": "9999999999"
        }
    }
}

如图所示,JSON包含一个嵌套元素contact

我们想将其与另一个由s2定义的JSON进行比较:

{
    "employee":
    {
        "id": "1212",
        "age": 34,
        "fullName": "John Miles",
        "contact":
        {
            "email": "[email protected]",
            "phone": "9999999999"
        }
    }
}

让我们将输入JSON作为JsonNode并比较:

assertEquals(mapper.readTree(s1), mapper.readTree(s2));

再次注意到,equals()方法也可以比较具有嵌套元素的两个输入JSON对象。

3.3. 比较包含列表元素的JSON对象

同样,我们还可以比较包含列表元素的两个JSON对象。

考虑这个定义为s1的JSON:

{
    "employee":
    {
        "id": "1212",
        "fullName": "John Miles",
        "age": 34,
        "skills": ["Java", "C++", "Python"]
    }
}

我们将它与另一个JSON,s2进行比较:

{
    "employee":
    {
        "id": "1212",
        "age": 34,
        "fullName": "John Miles",
        "skills": ["Java", "C++", "Python"] 
    } 
}

让我们将输入JSON作为JsonNode并比较:

assertEquals(mapper.readTree(s1), mapper.readTree(s2));

重要的是要知道,只有当两个列表元素在相同位置具有相同的值时,它们才会被认为是相等的。

4. 使用自定义比较器比较JSON对象

JsonNode.equals()在大多数情况下工作得很好。Jackson还提供了JsonNode.equals(comparator, JsonNode),以配置自定义Java比较器对象。

让我们了解如何使用自定义比较器。

4.1. 使用自定义比较器比较数值值

让我们看看如何使用自定义比较器来比较具有数值值的两个JSON元素。

我们将使用这个JSON作为输入s1

{
    "name": "John",
    "score": 5.0
}

我们将与另一个定义为s2的JSON进行比较:

{
    "name": "John",
    "score": 5
}

请注意,输入s1s2中的score属性值不相同。

让我们将输入JSON作为JsonNode进行比较:

JsonNode actualObj1 = mapper.readTree(s1);
JsonNode actualObj2 = mapper.readTree(s2);

assertNotEquals(actualObj1, actualObj2);

注意这两个对象不相等。标准的equals()方法认为5.0和5是不同的。

然而,我们可以使用自定义比较器来比较5和5.0,并将它们视为相等。

首先,我们创建一个比较两个NumericNode对象的比较器:

public class NumericNodeComparator implements Comparator<JsonNode> 
{
    @Override
    public int compare(JsonNode o1, JsonNode o2)
    {
        if (o1.equals(o2)){
           return 0;
        }
        if ((o1 instanceof NumericNode) && (o2 instanceof NumericNode)){
            Double d1 = ((NumericNode) o1).asDouble();
            Double d2 = ((NumericNode) o2).asDouble(); 
            if (d1.compareTo(d2) == 0) {
               return 0;
            }
        }
        return 1;
    }
}

接下来,让我们看看如何使用这个比较器:

NumericNodeComparator cmp = new NumericNodeComparator();
assertTrue(actualObj1.equals(cmp, actualObj2));

4.2. 使用自定义比较器比较文本值

让我们再看一个例子,演示如何使用自定义比较器进行不区分大小写的两个JSON值比较。

我们将使用这个JSON作为输入s1

{
    "name": "john", 
    "score": 5 
}

我们将与另一个定义为s2的JSON进行比较:

{ 
    "name": "JOHN", 
    "score": 5 
}

如图所示,name属性在输入s1中为小写,在s2中为大写。

首先,我们创建一个比较两个TextNode对象的比较器:

public class TextNodeComparator implements Comparator<JsonNode> 
{
    @Override
    public int compare(JsonNode o1, JsonNode o2) {
        if (o1.equals(o2)) {
            return 0;
        }
        if ((o1 instanceof TextNode) && (o2 instanceof TextNode)) {
            String s1 = ((TextNode) o1).asText();
            String s2 = ((TextNode) o2).asText();
            if (s1.equalsIgnoreCase(s2)) {
                return 0;
            }
        }
        return 1;
    }
}

让我们使用TextNodeComparator比较s1s2

JsonNode actualObj1 = mapper.readTree(s1);
JsonNode actualObj2 = mapper.readTree(s2);

TextNodeComparator cmp = new TextNodeComparator();

assertNotEquals(actualObj1, actualObj2);
assertTrue(actualObj1.equals(cmp, actualObj2));

最后,我们在比较两个JSON对象时使用自定义比较器对象非常有用,尤其是当输入JSON元素的值并不完全相同,但我们仍然希望将其视为相等的情况。

5. 结论

在这篇简短的文章中,我们了解了如何使用Jackson来比较两个JSON对象以及如何使用自定义比较器。

如往常一样,这里讨论的所有示例的完整源代码可以在GitHub上找到。