1. 概要

JSONはデータの文字列表現です。 アルゴリズムまたはテストでこのデータを比較したい場合があります。 また、JSONを含む文字列を比較することは可能ですが、文字列の比較は、コンテンツではなく、表現の違いに敏感です。

これを克服し、JSONデータを意味的に比較するには、空白などの影響やオブジェクトのキーの順序の影響を受けないメモリ内の構造にデータをロードする必要があります。

この短いチュートリアルでは、 Gson を使用してこれを解決します。これは、JSONオブジェクト間の詳細な比較を実行できるJSONシリアル化\逆シリアル化ライブラリです。

2. 異なる文字列で意味的に同一のJSON

私たちが解決しようとしている問題を詳しく見てみましょう。

同じJSONデータを表す2つの文字列があると仮定しますが、そのうちの1つには最後に余分なスペースがあります。

String string1 = "{\"fullName\": \"Emily Jenkins\", \"age\": 27    }";
String string2 = "{\"fullName\": \"Emily Jenkins\", \"age\": 27}";

JSONオブジェクトの内容は同じですが、上記を文字列として比較すると、違いがわかります。

assertNotEquals(string1, string2);

JSONは通常これに敏感ではありませんが、オブジェクト内のキーの順序が変更された場合も同じことが起こります。

String string1 = "{\"fullName\": \"Emily Jenkins\", \"age\": 27}";
String string2 = "{\"age\": 27, \"fullName\": \"Emily Jenkins\"}";
assertNotEquals(string1, string2);

これが、JSONデータを比較するためにJSON処理ライブラリを使用することでメリットが得られる理由です。

3. Mavenの依存関係

Gsonを使用するには、最初にGsonMaven依存関係を追加しましょう。

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.6</version>
</dependency>

4. JSONをGsonオブジェクトに解析する

オブジェクトの比較に入る前に、GsonがJavaでJSONデータをどのように表現するかを見てみましょう。

JavaでJSONを操作する場合、最初にJSON文字列をJavaオブジェクトに変換する必要があります。 Gsonは、ソースJSONをJsonElementツリーに解析するJsonParserを提供します。

JsonParser parser = new JsonParser();
String objectString = "{\"customer\": {\"fullName\": \"Emily Jenkins\", \"age\": 27 }}";
String arrayString = "[10, 20, 30]";

JsonElement json1 = parser.parse(objectString);
JsonElement json2 = parser.parse(arrayString);

JsonElement は抽象クラスであり、JSONの要素を表します。 parse メソッドは、JsonElementの実装を返します。 JsonObject、JsonArray、JsonPrimitiveまたはJsonNull:のいずれか

assertTrue(json1.isJsonObject());
assertTrue(json2.isJsonArray());

これらの各サブクラス( JsonObject、JsonArray、など)は、Object.equalsメソッドをオーバーライドし、効果的な詳細なJSON比較を提供します。

5. Gson比較のユースケース

5.1. 2つの単純なJSONオブジェクトを比較する

キーの順序が異なる単純なJSONオブジェクトを表す2つの文字列があるとします。

最初のオブジェクトは、ageより前のfullNameを持っています。

{
    "customer": {
        "id": 44521,
        "fullName": "Emily Jenkins",
        "age": 27
    }
}

2番目は順序を逆にします:

{
    "customer": {
        "id": 44521,
        "age": 27,
        "fullName": "Emily Jenkins"
    }
}

それらを簡単に解析して比較できます。

assertEquals(parser.parse(string1), parser.parse(string2));

この場合、JsonParserJsonObjectを返しますが、そのequals実装はorder-sensitiveではありません。

5.2. 2つのJSON配列を比較する

JSON配列の場合、JsonParserJsonArray。を返します。

1つの順序で1つの配列がある場合:

[10, 20, 30]
assertTrue(parser.parse(string1).isJsonArray());

別の順序で比較できます。

[20, 10, 30]

JsonObject とは異なり、 JsonArrayのequalsメソッドは順序依存であるため、これらの配列は等しくありません。これは意味的に正しいです。

assertNotEquals(parser.parse(string1), parser.parse(string2));

5.3. 2つのネストされたJSONオブジェクトを比較する

前に見たように、JsonParserはJSONのツリーのような構造を解析できます。 各JsonObjectおよびJsonArrayには、他の JsonElement オブジェクトが含まれています。これらのオブジェクトは、それ自体がJsonObjectまたはJsonArrayタイプである可能性があります。

equals を使用すると、すべてのメンバーが再帰的に比較されます。つまり、ネストされたオブジェクトも比較可能です:

これがstring1の場合:

{
  "customer": {
    "id": "44521",
    "fullName": "Emily Jenkins",
    "age": 27,
    "consumption_info": {
      "fav_product": "Coke",
      "last_buy": "2012-04-23"
    }
  }
}

そして、このJSONはstring2です。

{
  "customer": {
    "fullName": "Emily Jenkins",
    "id": "44521",
    "age": 27,
    "consumption_info": {
      "last_buy": "2012-04-23",
      "fav_product": "Coke"
   }
  }
}

次に、equalsメソッドを使用してそれらを比較できます。

assertEquals(parser.parse(string1), parser.parse(string2));

6. 結論

この短い記事では、JSONをStringとして比較する際の課題について説明しました。 Gsonを使用して、これらの文字列を解析して、比較をサポートするオブジェクト構造にする方法を見てきました。

いつものように、上記の例のソースコードはGitHubにあります。