Сравните два объекта JSON в Java
Я ищу библиотеку синтаксического анализа JSON, которая поддерживает сравнение двух объектов JSON, игнорирующих дочерний порядок, в частности для модульного тестирования JSON, возвращаемого из веб-службы.
поддерживает ли это какая-либо из основных библиотек JSON? Организация.библиотека json просто выполняет сравнение ссылок.
22 ответов:
в качестве общей архитектурной точки я обычно советую не позволять зависимостям от конкретного формата сериализации выходить за пределы вашего уровня хранения / сети; таким образом, я бы сначала рекомендовал вам рассмотреть возможность тестирования равенства между вашими собственными объектами приложения, а не их проявлениями JSON.
сказав это, я в настоящее время большой поклонник Джексон который мой быстрый читал их ObjectNode.равно () реализация предполагает ли установите сравнение членства, которое вы хотите:
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; }
попробовать Skyscreamer это JSONAssert.
его нестрогом режим имеет два основных преимущества, которые делают его менее хрупким:
- расширяемость объекта (например, с ожидаемым значением {id:1}, это все равно пройдет: {id:1, moredata:'x'}.)
- свободный порядок массива (например, ['собака', 'кошка']==['кошка', 'собака'])
в строгом режиме он ведет себя больше как тест 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 () call are expectedJSONString,actualDataString и isStrict.
результирующие сообщения довольно ясны, что важно при сравнении действительно больших объектов JSON.
С помощью 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);
Я бы сделал следующее,
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);
вы можете попробовать использовать json-lib JSONAssert класс:
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]>
если вы уже используете JUnit, последняя версия теперь использует Hamcrest. Это общая структура сопоставления (особенно полезная для модульного тестирования), которая может быть расширена для создания новых сопоставителей.
есть небольшая библиотека с открытым исходным кодом под названием
hamcrest-jsonс JSON-осведомленными совпадениями. Он хорошо документирован, протестирован и поддерживается. Ниже приведены некоторые полезные ссылки:пример кода с использованием объектов из библиотеки JSON
org.json.simple:Assert.assertThat( jsonObject1.toJSONString(), SameJSONAs.sameJSONAs(jsonObject2.toJSONString()));дополнительно, вы можете (1) разрешить массивы "любого порядка" и (2) игнорировать дополнительные поля.
так как существуют различные библиотеки JSON для Java (
Jackson,GSON,json-libи т. д.), полезно, чтоhamcrest-jsonподдерживает текст JSON (какjava.lang.String), а также изначально поддерживающие объекты из JSON Дугласа Крокфорда библиотекаorg.json.наконец, если вы не используете JUnit, вы можете использовать Hamcrest непосредственно для утверждений. (я писал об этом здесь.)
используйте эту библиотеку: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));
Вы можете попробовать JsonUnit. Он может сравнивать два объекта 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.
одна вещь, которую я сделал, и она творит чудеса, - это прочитать оба объекта в HashMap, а затем сравнить с обычным assertEquals(). Он вызовет метод equals () хэш-карт, который будет рекурсивно сравнивать все объекты внутри (они будут либо другими хэш-картами, либо каким-то одним объектом значения, например строкой или целым числом). Это было сделано с помощью codehaus ' Jackson JSON parser.
assertEquals(mapper.readValue(expectedJson, new TypeReference<HashMap<String, Object>>(){}), mapper.readValue(actualJson, new TypeReference<HashMap<String, Object>>(){}));аналогичный подход может быть использован, если объект JSON является массивом.
для 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; }
Я использую это, и отлично работает для меня (с орг.формат 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; } } }
Я бы взял библиотеку в http://json.org/java/, и изменить
equalsметод JSONObject и JSONArray для выполнения глубокого теста на равенство. Чтобы убедиться, что он работает без изменения порядка детей, все, что вам нужно сделать, это заменить внутреннюю карту наTreeMap, или использовать что-то вродеCollections.sort().
попробуйте это:
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; }
можно использовать zjsonpatch библиотека, которая представляет информацию diff в соответствии с RFC 6902 (JSON Patch). Его очень проста в использовании. Пожалуйста, посетите его страницу описания для его использования
Я знаю, что это обычно рассматривается только для тестирования, но вы можете использовать Hamcrest JSON comparitorSameJSONAs в Hamcrest JSON.
для таких, как я, желающих сделать это с Джексоном, вы можете использовать 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.
каратэ - это именно то, что вы ищете. Вот пример:
* def myJson = { foo: 'world', hey: 'ho', zee: [5], cat: { name: 'Billie' } } * match myJson = { cat: { name: 'Billie' }, hey: 'ho', foo: 'world', zee: [5] }(отказ от ответственности: dev здесь)
ничто другое, казалось, не работает совершенно правильно, поэтому я написал это:
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; }
следующий код будет более полезным для сравнения двух 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; }
для сравнения jsons я рекомендую использовать 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
JSON.areEqual(json1, json2); //using BlobCity Java Commonshttps://tech.blobcity.com/2018/09/02/json-equals-in-java-to-compare-two-jsons
Это решение для меня, работа очень хорошая:
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надеюсь, что это поможет другим.
Comments