python - recorrer - ¿Cómo comparar dos objetos JSON con los mismos elementos en un orden diferente igual?
que es un diccionario en python (5)
Descifralas y compáralas como comentario de mgilson.
El orden no importa para el diccionario siempre que las claves y los valores coincidan. (El diccionario no tiene orden en Python)
>>> {''a'': 1, ''b'': 2} == {''b'': 2, ''a'': 1}
True
Pero el orden es importante en la lista; La clasificación resolverá el problema de las listas.
>>> [1, 2] == [2, 1]
False
>>> [1, 2] == sorted([2, 1])
True
>>> a = ''{"errors": [{"error": "invalid", "field": "email"}, {"error": "required", "field": "name"}], "success": false}''
>>> b = ''{"errors": [{"error": "required", "field": "name"}, {"error": "invalid", "field": "email"}], "success": false}''
>>> a, b = json.loads(a), json.loads(b)
>>> a[''errors''].sort()
>>> b[''errors''].sort()
>>> a == b
True
El ejemplo anterior funcionará para el JSON en la pregunta. Para la solución general, vea la respuesta de Zero Piraeus.
¿Cómo puedo probar si dos objetos JSON son iguales en python, sin tener en cuenta el orden de las listas?
Por ejemplo ...
Documento JSON a :
{
"errors": [
{"error": "invalid", "field": "email"},
{"error": "required", "field": "name"}
],
"success": false
}
Documento JSON b :
{
"success": false,
"errors": [
{"error": "required", "field": "name"},
{"error": "invalid", "field": "email"}
]
}
a
y b
deben ser iguales, aunque el orden de las listas de "errors"
sea diferente.
Otra forma podría ser usar la json.dumps(X, sort_keys=True)
:
import json
a, b = json.dumps(a, sort_keys=True), json.dumps(b, sort_keys=True)
a == b # a normal string comparison
Esto funciona para diccionarios y listas anidadas.
Para los siguientes dos dictados ''dictWithListsInValue'' y ''reorderedDictWithReorderedListsInValue'', que simplemente son versiones reordenadas entre sí
dictObj = {"foo": "bar", "john": "doe"}
reorderedDictObj = {"john": "doe", "foo": "bar"}
dictObj2 = {"abc": "def"}
dictWithListsInValue = {''A'': [{''X'': [dictObj2, dictObj]}, {''Y'': 2}], ''B'': dictObj2}
reorderedDictWithReorderedListsInValue = {''B'': dictObj2, ''A'': [{''Y'': 2}, {''X'': [reorderedDictObj, dictObj2]}]}
a = {"L": "M", "N": dictWithListsInValue}
b = {"L": "M", "N": reorderedDictWithReorderedListsInValue}
print(sorted(a.items()) == sorted(b.items())) # gives false
Me dio resultado equivocado, es decir, falso.
Así que creé mi propio ObjectComparator de cutstom así:
def my_list_cmp(list1, list2):
if (list1.__len__() != list2.__len__()):
return False
for l in list1:
found = False
for m in list2:
res = my_obj_cmp(l, m)
if (res):
found = True
break
if (not found):
return False
return True
def my_obj_cmp(obj1, obj2):
if isinstance(obj1, list):
if (not isinstance(obj2, list)):
return False
return my_list_cmp(obj1, obj2)
elif (isinstance(obj1, dict)):
if (not isinstance(obj2, dict)):
return False
exp = set(obj2.keys()) == set(obj1.keys())
if (not exp):
# print(obj1.keys(), obj2.keys())
return False
for k in obj1.keys():
val1 = obj1.get(k)
val2 = obj2.get(k)
if isinstance(val1, list):
if (not my_list_cmp(val1, val2)):
return False
elif isinstance(val1, dict):
if (not my_obj_cmp(val1, val2)):
return False
else:
if val2 != val1:
return False
else:
return obj1 == obj2
return True
dictObj = {"foo": "bar", "john": "doe"}
reorderedDictObj = {"john": "doe", "foo": "bar"}
dictObj2 = {"abc": "def"}
dictWithListsInValue = {''A'': [{''X'': [dictObj2, dictObj]}, {''Y'': 2}], ''B'': dictObj2}
reorderedDictWithReorderedListsInValue = {''B'': dictObj2, ''A'': [{''Y'': 2}, {''X'': [reorderedDictObj, dictObj2]}]}
a = {"L": "M", "N": dictWithListsInValue}
b = {"L": "M", "N": reorderedDictWithReorderedListsInValue}
print(my_obj_cmp(a, b)) # gives true
Lo que me dio la salida correcta esperada!
La lógica es bastante simple:
Si los objetos son del tipo ''lista'', compare cada elemento de la primera lista con los elementos de la segunda lista hasta que se encuentre, y si el elemento no se encuentra después de pasar por la segunda lista, entonces ''encontrado'' sería = falso. se devuelve el valor ''encontrado''
De lo contrario, si los objetos que se van a comparar son del tipo ''dict'', compare los valores presentes para todas las claves respectivas en ambos objetos. (Se realiza comparación recursiva)
Si no, simplemente llame a obj1 == obj2. Por defecto, funciona bien para el objeto de cadenas y números y para esos eq () se define adecuadamente.
(Tenga en cuenta que el algoritmo puede mejorarse aún más eliminando los elementos que se encuentran en object2, de modo que el siguiente elemento de object1 no se compare con los elementos que ya se encuentran en el objeto2)
Puedes escribir tu propia función de iguales:
- los dicts son iguales si: 1) todas las claves son iguales, 2) todos los valores son iguales
- las listas son iguales si: todos los elementos son iguales y en el mismo orden
- primitivas son iguales si
a == b
Debido a que está tratando con json, tendrá tipos estándar de python: dict
, list
, etc., por lo que puede hacer una comprobación de tipo difícil if type(obj) == ''dict'':
etc.
Ejemplo aproximado (no probado):
def json_equals(jsonA, jsonB):
if type(jsonA) != type(jsonB):
# not equal
return false
if type(jsonA) == ''dict'':
if len(jsonA) != len(jsonB):
return false
for keyA in jsonA:
if keyA not in jsonB or not json_equal(jsonA[keyA], jsonB[keyA]):
return false
elif type(jsonA) == ''list'':
if len(jsonA) != len(jsonB):
return false
for itemA, itemB in zip(jsonA, jsonB)
if not json_equal(itemA, itemB):
return false
else:
return jsonA == jsonB
Si desea dos objetos con los mismos elementos pero en un orden diferente para comparar iguales, entonces lo más obvio es comparar copias ordenadas de ellos, por ejemplo, para los diccionarios representados por las cadenas JSON a
y b
:
import json
a = json.loads("""
{
"errors": [
{"error": "invalid", "field": "email"},
{"error": "required", "field": "name"}
],
"success": false
}
""")
b = json.loads("""
{
"success": false,
"errors": [
{"error": "required", "field": "name"},
{"error": "invalid", "field": "email"}
]
}
""")
>>> sorted(a.items()) == sorted(b.items())
False
... pero eso no funciona, porque en cada caso, el elemento de "errors"
del dict de nivel superior es una lista con los mismos elementos en un orden diferente, y sorted()
no intenta ordenar nada excepto El nivel "superior" de un iterable.
Para solucionarlo, podemos definir una función ordered
que ordenará recursivamente las listas que encuentre (y convertiremos los diccionarios en listas de pares (key, value)
para que sean ordenables):
def ordered(obj):
if isinstance(obj, dict):
return sorted((k, ordered(v)) for k, v in obj.items())
if isinstance(obj, list):
return sorted(ordered(x) for x in obj)
else:
return obj
Si aplicamos esta función a a
y b
, los resultados se comparan igual:
>>> ordered(a) == ordered(b)
True