web-dev-qa-db-ja.com

Javaでの子の順序を無視した2つのJSONオブジェクトの等価性のテスト

私は、子の順序を無視して2つのJSONオブジェクトを比較することをサポートするJSON解析ライブラリを探しています。特にWebサービスから返されるJSONの単体テストのためです。

これをサポートしている主要なJSONライブラリーはありますか? org.jsonライブラリは単純に参照比較を行います。

205
Jeff

一般的なアーキテクチャのポイントとして、私は通常、特定のシリアライゼーションフォーマットへの依存関係をあなたのストレージ/ネットワーキング層を越えて流出させないように勧めます。そのため、まずJSONの表現ではなく、自分のアプリケーションオブジェクト間の同等性をテストすることを検討することをお勧めします。

そうは言っても、私は現在 Jackson を大ファンにしています---それらを実装した ObjectNode.equals() を簡単に読んでみれば、セットメンバーシップの比較が欲しいのです。

public boolean equals(Object o)
{
    if (o == this) return true;
    if (o == null) return false;
    if (o.getClass() != getClass()) {
        return false;
    }
    ObjectNode other = (ObjectNode) o;
    if (other.size() != size()) {
        return false;
    }
    if (_children != null) {
        for (Map.Entry<String, JsonNode> en : _children.entrySet()) {
            String key = en.getKey();
            JsonNode value = en.getValue();

            JsonNode otherValue = other.get(key);

            if (otherValue == null || !otherValue.equals(value)) {
                return false;
            }
        }
    }
    return true;
}
73
Jolly Roger

Skyscreamerの JSONAssert を試してください。

その非厳密モードには、脆弱性を少なくする2つの大きな利点があります。

  • オブジェクトの拡張性(例:{id:1}の期待値では、これはまだ合格するでしょう:{id: 1、より多くのデータ: 'x'}。)
  • 配列の順序がゆるい(例:['dog'、 'cat'] == ['cat'、 'dog'])

厳密モードでは、json-libのテストクラスのように動作します。

テストは次のようになります。

@Test
public void testGetFriends() {
    JSONObject data = getRESTData("/friends/367.json");
    String expected = "{friends:[{id:123,name:\"Corby Page\"}"
        + ",{id:456,name:\"Solomon Duskis\"}]}";
    JSONAssert.assertEquals(expected, data, false);
}

JSONAssert.assertEquals()呼び出しのパラメーターは、expectedJSONStringactualDataStringです。 、およびisStrictです。

結果メッセージはかなり明確です。これは、実際に大きなJSONオブジェクトを比較するときに重要です。

134
Carter Page

GSONを使う

JsonParser parser = new JsonParser();
JsonElement o1 = parser.parse("{a : {a : 2}, b : 2}");
JsonElement o2 = parser.parse("{b : 2, a : {a : 2}}");
assertEquals(o1, o2);
40
axelhzf

私は次のようにします

JSONObject obj1 = /*json*/;
JSONObject obj2 = /*json*/;

ObjectMapper mapper = new ObjectMapper();

JsonNode tree1 = mapper.readTree(obj1.toString());
JsonNode tree2 = mapper.readTree(obj2.toString());

return tree1.equals(tree2);
23
josh

Json-libの JSONAssert classを使ってみてください。

JSONAssert.assertEquals(
  "{foo: 'bar', baz: 'qux'}",
  JSONObject.fromObject("{foo: 'bar', baz: 'xyzzy'}")
);

与えます:

junit.framework.ComparisonFailure: objects differed at key [baz]; expected:<[qux]> but was:<[xyzzy]>
13
hertzsprung

このライブラリを使用してください。 https://github.com/lukas-krecan/JsonUnit

Pom:

<dependency>
    <groupId>net.javacrumbs.json-unit</groupId>
    <artifactId>json-unit</artifactId>
    <version>1.5.0</version>
    <scope>test</scope>
</dependency>

IGNORING_ARRAY_ORDER - 配列内の順序を無視します

assertJsonEquals("{\"test\":[1,2,3]}",
  "{\"test\":  [3,2,1]}",
  when(IGNORING_ARRAY_ORDER)
);
13
chethu

あなたは JsonUnit を試すことができます。 2つのJSONオブジェクトを比較して相違点を報告できます。ジャクソンの上に建てられています。

例えば

assertJsonEquals("{\"test\":1}", "{\n\"test\": 2\n}");

の結果

Java.lang.AssertionError: JSON documents are different:
Different value found in node "test". Expected 1, got 2.
11
Lukas

すでにJUnitを使用している場合は、最新バージョンでHamcrestが採用されています。それは新しいマッチャーを構築するために拡張することができる(特に単体テストに有用な)一般的なマッチングフレームワークです。

JSONに対応したものを含むhamcrest-jsonと呼ばれる小さなオープンソースライブラリがあります。それはよく文書化され、テストされ、そしてサポートされています。以下は便利なリンクです。

JSONライブラリorg.json.simpleのオブジェクトを使用したコード例:

Assert.assertThat(
    jsonObject1.toJSONString(),
    SameJSONAs.sameJSONAs(jsonObject2.toJSONString()));

オプションとして、(1) "任意の順序"の配列を許可し、(2)余分なフィールドを無視することもできます。

Java用のさまざまなJSONライブラリ(JacksonGSONjson-libなど)があるため、hamcrest-jsonは(Java.lang.Stringとして)JSONテキストをサポートし、Douglas CrockfordのJSONライブラリorg.jsonからのオブジェクトをネイティブにサポートすることが役立ちます。

最後に、JUnitを使用していない場合は、Hamcrestを直接アサーションに使用できます。 ( ここで書きました

11
kevinarpe

私がしたことの一つは、両方のオブジェクトをHashMapに読み込んでから、通常のassertEquals()と比較することです。これはハッシュマップのequals()メソッドを呼び出します。これは内部のすべてのオブジェクトを再帰的に比較します(それらは他のハッシュマップか、文字列や整数のような単一の値のオブジェクトになります)。これはCodehausのJackson JSONパーサーを使って行われました。

assertEquals(mapper.readValue(expectedJson, new TypeReference<HashMap<String, Object>>(){}), mapper.readValue(actualJson, new TypeReference<HashMap<String, Object>>(){}));

JSONオブジェクトが代わりに配列の場合は、同様の方法を使用できます。

6
Claudio Aguiar

Org.jsonのために私は私自身のソリューション、JSONObjectインスタンスに匹敵するメソッドを展開しました。そのプロジェクトでは複雑なJSONオブジェクトを使用しなかったので、これがすべてのシナリオで機能するかどうかはわかりません。また、これを単体テストで使用していることを考えると、最適化には力を注いでいませんでした。ここにあります:

public static boolean jsonObjsAreEqual (JSONObject js1, JSONObject js2) throws JSONException {
    if (js1 == null || js2 == null) {
        return (js1 == js2);
    }

    List<String> l1 =  Arrays.asList(JSONObject.getNames(js1));
    Collections.sort(l1);
    List<String> l2 =  Arrays.asList(JSONObject.getNames(js2));
    Collections.sort(l2);
    if (!l1.equals(l2)) {
        return false;
    }
    for (String key : l1) {
        Object val1 = js1.get(key);
        Object val2 = js2.get(key);
        if (val1 instanceof JSONObject) {
            if (!(val2 instanceof JSONObject)) {
                return false;
            }
            if (!jsonObjsAreEqual((JSONObject)val1, (JSONObject)val2)) {
                return false;
            }
        }

        if (val1 == null) {
            if (val2 != null) {
                return false;
            }
        }  else if (!val1.equals(val2)) {
            return false;
        }
    }
    return true;
}
4
Victor Ionescu

私はこれを使用していて、私にとってはうまくいきます(org.json。*を使う):

package com.project1.helpers;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import Java.util.HashMap;
import Java.util.HashSet;
import Java.util.Iterator;
import Java.util.Map;
import Java.util.Set;

public class JSONUtils {

    public static boolean areEqual(Object ob1, Object ob2) throws JSONException {
        Object obj1Converted = convertJsonElement(ob1);
        Object obj2Converted = convertJsonElement(ob2);
        return obj1Converted.equals(obj2Converted);
    }

    private static Object convertJsonElement(Object elem) throws JSONException {
        if (elem instanceof JSONObject) {
            JSONObject obj = (JSONObject) elem;
            Iterator<String> keys = obj.keys();
            Map<String, Object> jsonMap = new HashMap<>();
            while (keys.hasNext()) {
                String key = keys.next();
                jsonMap.put(key, convertJsonElement(obj.get(key)));
            }
            return jsonMap;
        } else if (elem instanceof JSONArray) {
            JSONArray arr = (JSONArray) elem;
            Set<Object> jsonSet = new HashSet<>();
            for (int i = 0; i < arr.length(); i++) {
                jsonSet.add(convertJsonElement(arr.get(i)));
            }
            return jsonSet;
        } else {
            return elem;
        }
    }
}
4
catcher

空手 はまさにあなたが探しているものです。これが一例です。

* def myJson = { foo: 'world', hey: 'ho', zee: [5], cat: { name: 'Billie' } }
* match myJson = { cat: { name: 'Billie' }, hey: 'ho', foo: 'world', zee: [5] }

(免責事項:開発者はこちら)

2
Peter Thomas

私は http://json.org/Java/ にあるライブラリを利用して、JSONObjectとJSONArrayのequalsメソッドを修正して深い等価テストを行います。子の順序に関係なく確実に機能するようにするには、内部マップをTreeMapに置き換えるか、またはCollections.sort()のようなものを使用するだけです。

2
Yoni

これを試して:

public static boolean jsonsEqual(Object obj1, Object obj2) throws JSONException

    {
        if (!obj1.getClass().equals(obj2.getClass()))
        {
            return false;
        }

        if (obj1 instanceof JSONObject)
        {
            JSONObject jsonObj1 = (JSONObject) obj1;

            JSONObject jsonObj2 = (JSONObject) obj2;

            String[] names = JSONObject.getNames(jsonObj1);
            String[] names2 = JSONObject.getNames(jsonObj1);
            if (names.length != names2.length)
            {
                return false;
            }

            for (String fieldName:names)
            {
                Object obj1FieldValue = jsonObj1.get(fieldName);

                Object obj2FieldValue = jsonObj2.get(fieldName);

                if (!jsonsEqual(obj1FieldValue, obj2FieldValue))
                {
                    return false;
                }
            }
        }
        else if (obj1 instanceof JSONArray)
        {
            JSONArray obj1Array = (JSONArray) obj1;
            JSONArray obj2Array = (JSONArray) obj2;

            if (obj1Array.length() != obj2Array.length())
            {
                return false;
            }

            for (int i = 0; i < obj1Array.length(); i++)
            {
                boolean matchFound = false;

                for (int j = 0; j < obj2Array.length(); j++)
                {
                    if (jsonsEqual(obj1Array.get(i), obj2Array.get(j)))
                    {
                        matchFound = true;
                        break;
                    }
                }

                if (!matchFound)
                {
                    return false;
                }
            }
        }
        else
        {
            if (!obj1.equals(obj2))
            {
                return false;
            }
        }

        return true;
    }
2
Misha

JSONを比較するために私はJSONCompareを使用することをお勧めします。 https://github.com/fslev/json-compare

// Compare by regex
String expected = "{\"a\":\".*me.*\"}";
String actual = "{\"a\":\"some text\"}";
JSONCompare.assertEquals(expected, actual);  // True

// Check expected array has no extra elements
String expected = "[1,\"test\",4,\"!.*\"]";
String actual = "[4,1,\"test\"]";
JSONCompare.assertEquals(expected, actual);  // True

// Check expected array has no numbers
String expected = "[\"\\\\\\d+\"]";
String actual = "[\"text\",\"test\"]";
JSONCompare.assertEquals(expected, actual);  // True

// Check expected array has no numbers
String expected = "[\"\\\\\\d+\"]";
String actual = "[2018]";
JSONCompare.assertNotEquals(expected, actual);  // True
1
Slev Florin

RFC 6902(JSON Patch)に従って差分情報を表示する zjsonpatch libraryを使用できます。とても使いやすいです。使い方は説明ページをご覧ください。

1
z31415

私はそれが通常テストのためだけに考慮されることを知っていますが、あなたはHamcrest JSONのHamcrest JSONcomparitorSameJSONAsを使うことができます。

Hamcrest JSON SameJSONAs

1
Justin Ohms
JSON.areEqual(json1, json2); //using BlobCity Java Commons

https://tech.blobcity.com/2018/09/02/json-equals-in-Java-to-compare-two-jsons

0
Sanket Sarang

私がJacksonでこれをやりたいのなら、 json-unit を使うことができます。

JsonAssert.assertJsonEquals(jsonNode1, jsonNode2);

エラーは、不一致の種類に関する有用なフィードバックを提供します。

Java.lang.AssertionError: JSON documents have different values:
Different value found in node "heading.content[0].tag[0]". Expected 10209, got 10206.
0
krookedking

他には何も正しく動作しているように見えなかったので、私はこれを書いた:

private boolean jsonEquals(JsonNode actualJson, JsonNode expectJson) {
    if(actualJson.getNodeType() != expectJson.getNodeType()) return false;

    switch(expectJson.getNodeType()) {
    case NUMBER:
        return actualJson.asDouble() == expectJson.asDouble();
    case STRING:
    case BOOLEAN:
        return actualJson.asText().equals(expectJson.asText());
    case OBJECT:
        if(actualJson.size() != expectJson.size()) return false;

        Iterator<String> fieldIterator = actualJson.fieldNames();
        while(fieldIterator.hasNext()) {
            String fieldName = fieldIterator.next();
            if(!jsonEquals(actualJson.get(fieldName), expectJson.get(fieldName))) {
                return false;
            }
        }
        break;
    case ARRAY:
        if(actualJson.size() != expectJson.size()) return false;
        List<JsonNode> remaining = new ArrayList<>();
        expectJson.forEach(remaining::add);
        // O(N^2)   
        for(int i=0; i < actualJson.size(); ++i) {
            boolean oneEquals = false;
            for(int j=0; j < remaining.size(); ++j) {
                if(jsonEquals(actualJson.get(i), remaining.get(j))) {
                    oneEquals = true;
                    remaining.remove(j);
                    break;
                }
            }
            if(!oneEquals) return false;
        }
        break;
    default:
        throw new IllegalStateException();
    }
    return true;
}
0
Alex R

次のコードは、2つのJsonObject、JsonArray、JsonPrimitive、およびJasonElementsを比較するのに役立ちます。

private boolean compareJson(JsonElement json1, JsonElement json2) {
        boolean isEqual = true;
        // Check whether both jsonElement are not null
        if (json1 != null && json2 != null) {

            // Check whether both jsonElement are objects
            if (json1.isJsonObject() && json2.isJsonObject()) {
                Set<Entry<String, JsonElement>> ens1 = ((JsonObject) json1).entrySet();
                Set<Entry<String, JsonElement>> ens2 = ((JsonObject) json2).entrySet();
                JsonObject json2obj = (JsonObject) json2;
                if (ens1 != null && ens2 != null) {
                    // (ens2.size() == ens1.size())
                    // Iterate JSON Elements with Key values
                    for (Entry<String, JsonElement> en : ens1) {
                        isEqual = isEqual && compareJson(en.getValue(), json2obj.get(en.getKey()));
                    }
                } else {
                    return false;
                }
            }

            // Check whether both jsonElement are arrays
            else if (json1.isJsonArray() && json2.isJsonArray()) {
                JsonArray jarr1 = json1.getAsJsonArray();
                JsonArray jarr2 = json2.getAsJsonArray();
                if (jarr1.size() != jarr2.size()) {
                    return false;
                } else {
                    int i = 0;
                    // Iterate JSON Array to JSON Elements
                    for (JsonElement je : jarr1) {
                        isEqual = isEqual && compareJson(je, jarr2.get(i));
                        i++;
                    }
                }
            }

            // Check whether both jsonElement are null
            else if (json1.isJsonNull() && json2.isJsonNull()) {
                return true;
            }

            // Check whether both jsonElement are primitives
            else if (json1.isJsonPrimitive() && json2.isJsonPrimitive()) {
                if (json1.equals(json2)) {
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        } else if (json1 == null && json2 == null) {
            return true;
        } else {
            return false;
        }
        return isEqual;
    }
0
Radadiya Nikunj