1. 概要

In this tutorial, we’ll have a look at comparing two JSON objects using Jackson — a JSON processing library for Java.

2. Mavenの依存関係

まず、 jackson-databindMavenの依存関係を追加しましょう。

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

3. Jacksonを使用して2つのJSONオブジェクトを比較する

We’ll be using the ObjectMapper class to read an object as a JsonNode.

ObjectMapperを作成しましょう。

ObjectMapper mapper = new ObjectMapper();

3.1. 2つの単純な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変数s1とs2の属性の順序は同じではありませんが、equals()メソッドは順序を無視し、それらを等しいものとして扱うことに注意することが重要です。

3.2. Compare Two JSON Objects With a Nested Element

次に、ネストされた要素を持つ2つのJSONオブジェクトを比較する方法を説明します。

s1変数として定義されたJSONから始めましょう。

{ 
    "employee":
    {
        "id": "1212",
        "fullName":"John Miles",
        "age": 34,
        "contact":
        {
            "email": "john@xyz.com",
            "phone": "9999999999"
        }
    }
}

As we can see, the JSON contains a nested element contact.

We want to compare it with another JSON defined by s2:

{
    "employee":
    {
        "id": "1212",
        "age": 34,
        "fullName": "John Miles",
        "contact":
        {
            "email": "john@xyz.com",
            "phone": "9999999999"
        }
    }
}

入力JSONをJsonNodeとして読み取り、比較してみましょう。

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

繰り返しになりますが、 equals()は2つの入力JSONオブジェクトをネストされた要素と比較することもできます。

3.3. リスト要素を含む2つのJSONオブジェクトを比較する

同様に、list要素を含む2つのJSONオブジェクトを比較することもできます。

s1として定義されているこのJSONについて考えてみましょう。

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

We are comparing it with another JSON, s2:

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

入力JSONをJsonNodeとして読み取り、比較してみましょう。

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

2つのリスト要素は、まったく同じ順序で同じ値を持つ場合にのみ、等しいものとして比較されることを知っておくことが重要です。

4. Compare Two JSON Objects With a Custom Comparator

JsonNode.equals works quite well in most cases. Jackson also provides JsonNode.equals(comparator, JsonNode) to configure a custom Java Comparator object.

Let’s understand how to use a custom Comparator.

4.1. 数値を比較するカスタムコンパレータ

Let’s look at how to use a custom Comparator to compare two JSON elements having numeric values.

このJSONを入力s1として使用します。

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

s2として定義されている別のJSONと比較してみましょう。

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

私たちはそれを観察する必要があります入力s1とs2の属性スコアの値は同じではありません。

入力JSONをJsonNodeとして読み取り、比較してみましょう。

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

assertNotEquals(actualObj1, actualObj2);

Notice that the two objects are not equal. 標準のequals()メソッドは、値5.0と5を異なるものと見なします。

However, we can use a custom Comparator to compare values 5 and 5.0 and treat them as equal.

最初にコンパレータを作成して、2つの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. テキスト値を比較するカスタムコンパレータ

2つのJSON値の大文字と小文字を区別しない比較用のカスタムコンパレータの別の例を見てみましょう。

このJSONを入力s1として使用します。

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

s2として定義されている別のJSONと比較してみましょう。

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

As we can see, the attribute name is lowercase in input s1 and uppercase in s2.

最初にコンパレータを作成して、2つの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;
    }
}

Let’s see how to compare s1 and s2 using TextNodeComparator:

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

TextNodeComparator cmp = new TextNodeComparator();

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

最後に、2つのJSONオブジェクトを比較しながらカスタムコンパレータオブジェクトを使用していることがわかりますは、入力JSON要素の値が完全に同じではないが、それらを等しいものとして扱いたい場合に非常に役立ちます。

5. 結論

In this quick article, we’ve seen how to use Jackson to compare two JSON objects and use a custom comparator. 

As always, the full source code of all the examples discussed here can be found over on GitHub.