Comparer deux objets JSON en Java
je suis à la recherche d'une bibliothèque JSON parsing qui soutient la comparaison de deux objets JSON ignorant commande d'enfant, spécifiquement pour l'Unité de test JSON revenant d'un service web.
est-ce que l'une des principales bibliothèques JSON soutient ceci? Le org.la bibliothèque json fait simplement une comparaison de référence.
22 réponses
en tant que point d'architecture général, je déconseille habituellement de laisser les dépendances sur un format de sérialisation particulier se vider de leur substance au-delà de votre couche de stockage/réseau; ainsi, je recommande d'abord que vous envisagiez de tester l'égalité entre vos propres objets d'application plutôt que leurs manifestations JSON.
ayant dit cela, je suis actuellement un grand fan de Jackson qui ma lecture rapide de leur ObjectNode.égal () la mise en œuvre suggère l'appartenance de comparaison que vous voulez:
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;
}
Essayer Skyscreamer JSONAssert .
Its non-strict le mode présente deux avantages majeurs qui le rendent moins fragile:
- extensibilité de L'objet (par exemple avec une valeur attendue de {id:1} , cela passerait encore: {id:1,moredata:'x'} .)
- Lâche tableau de commande (par exemple, ['chien','cat']==['cat','chien'])
en mode strict, il se comporte plus comme la classe test de json-lib.
un test ressemble à quelque chose comme ceci:
@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);
}
les paramètres du JSONAssert.assertEquals() appel sont expectedJSONString , actualDataString , et isStrict .
les messages de résultat sont assez clairs, ce qui est important lorsque l'on compare De Très gros objets JSON.
utilisant 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);
je voudrais faire le suivant,
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);
vous pouvez essayer d'utiliser json-lib's JSONAssert classe:
JSONAssert.assertEquals(
"{foo: 'bar', baz: 'qux'}",
JSONObject.fromObject("{foo: 'bar', baz: 'xyzzy'}"));
donne:
junit.framework.ComparisonFailure: objects differed at key [baz]; expected:<[qux]> but was:<[xyzzy]>
si vous utilisez déjà JUnit, la dernière version utilise maintenant Hamcrest. Il s'agit d'un cadre d'appariement Générique (particulièrement utile pour les tests unitaires) qui peut être étendu pour construire de nouveaux matchers.
il y a une petite bibliothèque open source appelée hamcrest-json
avec des correspondances JSON-aware. Il est bien documenté, testé et soutenu. Voici quelques liens utiles:
exemple de code utilisant des objets de la bibliothèque JSON org.json.simple
:
Assert.assertThat(
jsonObject1.toJSONString(),
SameJSONAs.sameJSONAs(jsonObject2.toJSONString()));
en option, vous pouvez (1) autoriser les tableaux" n'importe quel ordre " et (2) ignorer les champs supplémentaires.
Puisqu'il existe une variété de bibliothèques JSON pour Java ( Jackson
, GSON
, json-lib
, etc.), il est utile que hamcrest-json
supporte le texte de JSON( comme java.lang.String
), ainsi que les objets supportant nativement de la bibliothèque JSON de Douglas Crockford org.json
.
enfin, si vous n'utilisez pas JUnit, vous pouvez utiliser Hamcrest directement pour les assertions. ( j'ai écrit à ce sujet ici. )
utilisez cette bibliothèque: 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-ignore l'ordre dans les tableaux
assertJsonEquals("{\"test\":[1,2,3]}",
"{\"test\": [3,2,1]}",when(IGNORING_ARRAY_ORDER));
vous pouvez essayer JsonUnit . Il peut comparer deux objets JSON et signaler des différences. Il est construit sur Jackson.
par exemple
assertJsonEquals("{\"test\":1}", "{\n\"test\": 2\n}");
résultats dans
java.lang.AssertionError: JSON documents are different:
Different value found in node "test". Expected 1, got 2.
une chose que j'ai faite et qui fait des Merveilles est de lire les deux objets dans HashMap puis de les comparer avec un assertEquals régulier(). Il appellera la méthode equals () des hashmaps, qui comparera récursivement tous les objets à l'intérieur (ils seront soit d'autres hashmaps ou un objet à une seule valeur comme une chaîne ou un entier). Cela a été fait en utilisant Codehaus ' Jackson JSON parser.
assertEquals(mapper.readValue(expectedJson, new TypeReference<HashMap<String, Object>>(){}), mapper.readValue(actualJson, new TypeReference<HashMap<String, Object>>(){}));
une approche similaire peut être utilisée si L'objet JSON est un tableau à la place.
pour org.json j'ai déployé ma propre solution, une méthode qui se compare aux instances de JSONObject. Je n'ai pas travaillé avec des objets JSON complexes dans ce projet, donc je ne sais pas si cela fonctionne dans tous les scénarios. De plus, étant donné que je l'utilise dans les tests unitaires, je n'ai pas mis d'effort dans les optimisations. Le voici:
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;
}
j'utilise ceci, et fonctionne très bien pour moi (avec 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;
}
}
}
je prendrais la bibliothèque à http://json.org/java / , et modifier la méthode equals
de JSONObject et JSONArray pour faire un test d'égalité profonde. Pour s'assurer que cela fonctionne sans se dégrader de l'ordre des enfants, tout ce que vous devez faire est de remplacer la carte intérieure avec un TreeMap
, ou utiliser quelque chose comme Collections.sort()
.
essayez ceci:
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;
}
vous pouvez utiliser zjsonpatch bibliothèque, qui présente les informations diff conformément à la RFC 6902 (JSON Patch). Il est très facile à utiliser. S'il vous plaît visitez sa page de description pour son usage
je sais qu'il est généralement considéré uniquement pour les tests, mais vous pourriez utiliser le Hamcrest JSON comparitorSameJSONAs dans Hamcrest JSON.
Pour ceux comme moi qui veulent le faire avec Jackson, vous pouvez utiliser json-unité .
JsonAssert.assertJsonEquals(jsonNode1, jsonNode2);
Les erreurs de donner une rétroaction utile sur le type de décalage:
java.lang.AssertionError: JSON documents have different values:
Different value found in node "heading.content[0].tag[0]". Expected 10209, got 10206.
Karaté est exactement ce que vous recherchez. Voici un exemple:
* def myJson = { foo: 'world', hey: 'ho', zee: [5], cat: { name: 'Billie' } }
* match myJson = { cat: { name: 'Billie' }, hey: 'ho', foo: 'world', zee: [5] }
(avertissement: dev ici)
rien d'autre ne semblait fonctionner correctement, alors j'ai écrit ceci:
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;
}
code suivant sera plus utile pour comparer deux JsonObject, JsonArray, JsonPrimitive et 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;
}
pour comparer les jsons je recommande d'utiliser JSON-compare: 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
JSON.areEqual(json1, json2); //using BlobCity Java Commons
https://tech.blobcity.com/2018/09/02/json-equals-in-java-to-compare-two-jsons
Cette Solution pour moi, le travail est très bon:
try {
// Getting The Array "Courses" from json1 & json2
Courses1 =json1.getJSONArray(TAG_COURSES1);
Courses2 = json2.getJSONArray(TAG_COURSES);
//LOOP FOR JSON1
for(int i = 0; i < Courses1.length(); i++){
//LOOP FOR JSON2
for(int ii = 0; ii < Courses2.length(); ii++){
JSONObject courses1 = Courses1.getJSONObject(i);
JSONObject courses2 = Courses2.getJSONObject(ii);
// Storing each json1 item in variable
int courseID1 = courses1.getInt(TAG_COURSEID1);
Log.e("COURSEID2:", Integer.toString(courseID1));
String Rating1 = courses1.getString(TAG_RATING1);
int Status1 = courses1.getInt(TAG_STATUS1);
Log.e("Status1:", Integer.toString(Status1)); //Put the actual value for Status1 in log.
// Storing each json2 item in variable
int courseID2 = courses2.getInt(TAG_COURSEID);
Log.e("COURSEID2:", Integer.toString(courseID)); //Put the actual value for CourseID in log
String Title2 = courses2.getString(TAG_TITLE);
String instructor2 = courses2.getString(TAG_INSTRUCTOR);
String length2 = courses2.getString(TAG_LENGTH);
String rating2 = courses2.getString(TAG_RATING);
String subject2 = courses2.getString(TAG_SUBJECT);
String description2 = courses2.getString(TAG_DESCRIPTION);
//Status1 = 5 from json1; Incomplete, Status1 =-1 Complete
if(Status1 == 5 && courseID2 == courseID1){
// creating new HashMap
HashMap<String, String> map = new HashMap<String, String>();
//Storing the elements if condition is true.
map.put(TAG_COURSEID, Integer.toString(courseID2)); //pend for compare
map.put(TAG_TITLE, Title2);
map.put(TAG_INSTRUCTOR, instructor2);
map.put(TAG_LENGTH, length2);
map.put(TAG_RATING, rating2);
map.put(TAG_SUBJECT, subject2); //show it
map.put(TAG_DESCRIPTION, description2);
//adding HashList to ArrayList
contactList.add(map);
}//if
}//for2 (json2)
} //for1 (json1)
}//Try
Espérons que cela aide les autres.