pasar - recorrer un json en java
Compara dos objetos JSON en Java (21)
Estoy buscando una biblioteca de análisis JSON que admita la comparación de dos objetos JSON que ignoran el orden secundario, específicamente para la prueba unitaria de JSON que regresa de un servicio web.
¿Alguna de las principales bibliotecas JSON admite esto? La biblioteca org.json simplemente hace una comparación de referencia.
Como punto arquitectónico general, generalmente desaconsejo dejar que las dependencias de un formato de serialización particular se desborden más allá de su capa de almacenamiento / red; por lo tanto, primero recomiendo que considere probar la igualdad entre sus propios objetos de aplicación en lugar de sus manifestaciones JSON.
Habiendo dicho eso, actualmente soy un gran admirador de Jackson y mi rápida lectura de la implementación de ObjectNode.equals () sugiere la comparación de membresía establecida que desea:
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;
}
El siguiente código será más útil para comparar dos JsonObject, JsonArray, JsonPrimitive y 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;
}
Esta solución para mí, el trabajo es muy bueno:
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
Espero que esto ayude a otros.
Estoy usando esto, y funciona bien para mí (con 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;
}
}
}
Nada más parecía funcionar del todo bien, así que escribí esto:
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;
}
Para aquellos que quieran hacer esto con Jackson, pueden usar https://github.com/lukas-krecan/JsonUnit .
JsonAssert.assertJsonEquals(jsonNode1, jsonNode2);
Los errores proporcionan información útil sobre el tipo de desajuste:
java.lang.AssertionError: JSON documents have different values:
Different value found in node "heading.content[0].tag[0]". Expected 10209, got 10206.
Para org.json, he desarrollado mi propia solución, un método que se compara con las instancias de JSONObject. No trabajé con objetos JSON complejos en ese proyecto, así que no sé si esto funciona en todos los escenarios. Además, dado que uso esto en pruebas unitarias, no puse esfuerzo en las optimizaciones. Aquí está:
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;
}
Podría intentar usar la clase JSONAssert json-lib:
JSONAssert.assertEquals(
"{foo: ''bar'', baz: ''qux''}",
JSONObject.fromObject("{foo: ''bar'', baz: ''xyzzy''}"));
Da:
junit.framework.ComparisonFailure: objects differed at key [baz]; expected:<[qux]> but was:<[xyzzy]>
Prueba el JSONAssert de JSONAssert .
Su modo no estricto tiene dos ventajas principales que lo hacen menos frágil:
- La extensibilidad del objeto (por ejemplo, con un valor esperado de {id: 1} , esto aún pasaría: {id: 1, moredata: ''x''} .)
- Ordenamiento de arreglo suelto (ej. [''Dog'', ''cat''] == [''cat'', ''dog''])
En modo estricto se comporta más como la clase de prueba de json-lib.
Una prueba se ve así:
@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);
}
Los parámetros en la llamada JSONAssert.assertEquals () se esperanJSONString , actualDataString e isStrict .
Los mensajes resultantes son bastante claros, lo cual es importante cuando se comparan objetos JSON realmente grandes.
Prueba esto:
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;
}
Puede usar la biblioteca zjsonpatch , que presenta la información de diferencia de acuerdo con RFC 6902 (revisión JSON). Es muy fácil de usar. Por favor visite su página de descripción para su uso
Puedes probar https://github.com/lukas-krecan/JsonUnit . Puede comparar dos objetos JSON e informar diferencias. Está construido encima de Jackson.
Por ejemplo
assertJsonEquals("{/"test/":1}", "{/n/"test/": 2/n}");
Resultados en
java.lang.AssertionError: JSON documents are different:
Different value found in node "test". Expected 1, got 2.
Sé que generalmente se considera solo para pruebas, pero puede usar el comparador Hamson JSON de Hamcrest JSON.
Si ya está utilizando JUnit, la última versión ahora emplea Hamcrest. Es un marco de coincidencia genérico (especialmente útil para las pruebas unitarias) que se puede ampliar para crear nuevos equilibradores.
Hay una pequeña biblioteca de código abierto llamada hamcrest-json
con coincidencias compatibles con JSON. Está bien documentado, probado y respaldado. A continuación se encuentran algunos enlaces útiles:
Código de ejemplo que usa objetos de la biblioteca JSON org.json.simple
:
Assert.assertThat(
jsonObject1.toJSONString(),
SameJSONAs.sameJSONAs(jsonObject2.toJSONString()));
Opcionalmente, puede (1) permitir matrices de "cualquier orden" y (2) ignorar campos adicionales.
Dado que hay una gran variedad de bibliotecas JSON para Java ( Jackson
, GSON
, json-lib
, etc.), es útil que hamcrest-json
compatible con texto JSON (como java.lang.String
), así como con el soporte nativo de objetos de Douglas. Crockford''s JSON library org.json
.
Finalmente, si no está utilizando JUnit, puede usar Hamcrest directamente para las afirmaciones. ( Escribí sobre esto aquí )
Tomaría la biblioteca en http://json.org/java/ y modificaré el método equals
de JSONObject y JSONArray para hacer una prueba profunda de igualdad. Para asegurarse de que funciona sin graduación del orden de los niños, todo lo que necesita hacer es reemplazar el mapa interno con un TreeMap
o usar algo como Collections.sort()
.
Una cosa que hice y que funciona de maravillas es leer ambos objetos en HashMap y luego compararlos con un assertEquals () regular. Llamará al método equals () de los hashmaps, que comparará recursivamente todos los objetos que están dentro (serán otros hashmaps o algún objeto de valor único como una cadena o un entero). Esto se hizo usando el analizador Jackson JSON de Codehaus.
assertEquals(mapper.readValue(expectedJson, new TypeReference<HashMap<String, Object>>(){}), mapper.readValue(actualJson, new TypeReference<HashMap<String, Object>>(){}));
Se puede usar un enfoque similar si el objeto JSON es una matriz en su lugar.
Usando 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);
Utilice esta biblioteca: 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 - ignora el orden en las matrices
assertJsonEquals("{/"test/":[1,2,3]}",
"{/"test/": [3,2,1]}",when(IGNORING_ARRAY_ORDER));
Yo haría lo siguiente,
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);
jsonObject implementa una interfaz comparable, intenta usar collection.sort ().
Karate es exactamente lo que estás buscando. Aquí hay un ejemplo:
* def myJson = { foo: ''world'', hey: ''ho'', zee: [5], cat: { name: ''Billie'' } }
* match myJson = { cat: { name: ''Billie'' }, hey: ''ho'', foo: ''world'', zee: [5] }
(descargo de responsabilidad: dev aquí)