wake-up-neo.com

Testen Sie zwei JSON-Objekte auf Gleichheit, wobei die untergeordnete Reihenfolge in Java ignoriert wird

Ich bin auf der Suche nach einer JSON-Parsing-Bibliothek, die den Vergleich von zwei JSON-Objekten unterstützt, wobei die untergeordnete Reihenfolge ignoriert wird, insbesondere für JSON-Tests, die von einem Webservice zurückgegeben werden.

Unterstützt eine der großen JSON-Bibliotheken dies? Die org.json-Bibliothek führt einfach einen Referenzvergleich durch.

191
Jeff

Generell empfehle ich generell, dass Abhängigkeiten von einem bestimmten Serialisierungsformat nicht über Ihre Speicher-/Netzwerkschicht hinausgehen. Daher würde ich zuerst empfehlen, die Gleichheit zwischen Ihren eigenen Anwendungsobjekten und nicht deren JSON-Manifestationen zu testen.

Trotzdem bin ich momentan ein großer Fan von Jackson , was meine schnelle Lektüre ihrer ObjectNode.equals () -Umsetzung nahelegt, schlägt der festgelegte Mitgliedschaftsvergleich vor:

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;
}
72
Jolly Roger

Probieren Sie Skyscreamer's JSONAssert aus.

Der nicht-strikte -Modus hat zwei Hauptvorteile, die ihn weniger spröde machen:

  • Objekterweiterbarkeit (z. B. Bei einem erwarteten Wert von {id: 1} würde dies immer noch passieren: {id: 1, moredata: 'x'}.)
  • Lose Array-Reihenfolge (z. B. ["Hund", "Katze"] == ["Katze", "Hund"])

Im strikten Modus verhält es sich eher wie die Testklasse von json-lib.

Ein Test sieht ungefähr so ​​aus:

@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);
}

Die Parameter im Aufruf von JSONAssert.assertEquals () sind expectedJSONString, actualDataString und isStrict.

Die Ergebnisnachrichten sind ziemlich klar, was beim Vergleich von wirklich großen JSON-Objekten wichtig ist.

130
Carter Page

GSON verwenden

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);
38
axelhzf

Ich würde folgendes tun,

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

final ObjectMapper mapper = new ObjectMapper();

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

return tree1.equals(tree2);
21
joshu

Sie könnten versuchen, Json-libs JSONAssert class zu verwenden:

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

Gibt:

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

Sie können JsonUnit versuchen. Es kann zwei JSON-Objekte vergleichen und Unterschiede melden. Es ist auf Jackson gebaut.

Zum Beispiel 

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

Ergebnisse in

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

Wenn Sie JUnit bereits verwenden, verwendet die neueste Version jetzt Hamcrest. Es ist ein generisches Matching-Framework (besonders nützlich für Unit-Tests), das zum Erstellen neuer Matcher erweitert werden kann.

Es gibt eine kleine Open Source-Bibliothek namens hamcrest-json mit Übereinstimmungen mit JSON-Unterstützung. Es ist gut dokumentiert, getestet und unterstützt. Nachfolgend einige nützliche Links:

Beispielcode mit Objekten aus der JSON-Bibliothek org.json.simple:

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

Optional können Sie (1) Arrays "any order" zulassen und (2) zusätzliche Felder ignorieren.

Da es eine Vielzahl von JSON-Bibliotheken für Java gibt (Jackson, GSON, json-lib usw.), ist es nützlich, dass hamcrest-json JSON-Text (als Java.lang.String) unterstützt sowie Objekte aus der Douglas Crockford-JSON-Bibliothek org.json nativ unterstützt.

Wenn Sie JUnit nicht verwenden, können Sie Hamcrest direkt für Assertions verwenden. ( Ich habe hier darüber geschrieben. )

11
kevinarpe

Verwenden Sie diese Bibliothek: 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 - ignoriert die Reihenfolge in Arrays

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

Eine Sache, die ich getan habe und die Wunder wirkt, ist, beide Objekte in HashMap einzulesen und dann mit einem normalen assertEquals () zu vergleichen. Es wird die equals () -Methode der Hashmaps aufgerufen, die alle darin enthaltenen Objekte rekursiv vergleicht (sie sind entweder andere Hashmaps oder ein einzelnes Wertobjekt wie ein String oder eine Ganzzahl). Dies wurde mit dem Jackson JSON-Parser von Codehaus durchgeführt.

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

Ein ähnlicher Ansatz kann verwendet werden, wenn das JSON-Objekt stattdessen ein Array ist.

6
Claudio Aguiar

Für org.json habe ich meine eigene Lösung eingeführt, eine Methode, die mit JSONObject-Instanzen vergleichbar ist. Ich habe in diesem Projekt nicht mit komplexen JSON-Objekten gearbeitet, daher weiß ich nicht, ob dies in allen Szenarien funktioniert. Da ich dies in Einzeltests verwende, habe ich mich auch nicht mit Optimierungen beschäftigt. Hier ist es:

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

Ich benutze das und funktioniert gut für mich (mit 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

Karate ist genau das, wonach du suchst. Hier ist ein Beispiel:

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

(Haftungsausschluss: dev hier)

2
Peter Thomas

Ich würde die Bibliothek unter http://json.org/Java/ nehmen und die equals-Methode von JSONObject und JSONArray ändern, um einen tiefen Gleichheitstest durchzuführen. Um sicherzustellen, dass die Funktion der Kinder ohne Absturz funktioniert, müssen Sie nur die innere Karte durch eine TreeMap ersetzen oder etwas wie Collections.sort() verwenden.

2
Yoni

Versuche dies:

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

Zum Vergleich von jsons empfehle ich die Verwendung von 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

Sie können die Bibliothek zjsonpatch verwenden, die die Differenzinformationen gemäß RFC 6902 (JSON-Patch) darstellt. Es ist sehr einfach zu bedienen. Bitte besuchen Sie die Beschreibungsseite für ihre Verwendung

1
z31415

Ich weiß, dass es normalerweise nur zum Testen in Betracht kommt, aber Sie könnten den Hamcrest JSON comparitorSameJSONAs in Hamcrest JSON verwenden.

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

Für diejenigen wie mich, die dies mit Jackson machen wollen, können Sie json-unit verwenden.

JsonAssert.assertJsonEquals(jsonNode1, jsonNode2);

Die Fehler geben nützliche Rückmeldung über die Art der Nichtübereinstimmung:

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

Nichts anderes schien richtig zu funktionieren, also schrieb ich folgendes:

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

Der folgende Code ist hilfreicher, um zwei JsonObject, JsonArray, JsonPrimitive und JasonElements zu vergleichen.

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