같은 요소를 가진 두 JSON 개체를 다른 순서로 비교하려면 어떻게 해야 합니까?
목록의 순서를 무시하고 두 개의 JSON 개체가 python에서 동일한지 테스트하려면 어떻게 해야 합니까?
예를 들어...
JSON 문서 a:
{
"errors": [
{"error": "invalid", "field": "email"},
{"error": "required", "field": "name"}
],
"success": false
}
JSON 문서 b:
{
"success": false,
"errors": [
{"error": "required", "field": "name"},
{"error": "invalid", "field": "email"}
]
}
a
그리고.b
동등하게 비교해야 한다.비록 순서는"errors"
리스트가 다릅니다.
같은 요소를 가지지만 순서가 다른 두 개체를 비교하려면 JSON 문자열로 표시되는 사전 등의 정렬된 복사본을 비교해야 합니다.a
그리고.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
하지만 그건 통하지 않아 왜냐면 각각의 경우엔"errors"
top-level dict의 항목은 동일한 요소를 다른 순서로 가진 목록입니다.sorted()
반복할 수 있는 "최상위" 수준 이외에는 아무 것도 분류하려고 하지 않습니다.
이 문제를 해결하기 위해ordered
발견된 목록을 반복적으로 정렬하는 함수(및 사전을 목록으로 변환)(key, value)
쌍으로 주문 가능):
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
이 기능을 다음에 적용하면a
그리고.b
비교 결과는 다음과 같습니다.
>>> ordered(a) == ordered(b)
True
다른 방법으로는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
이는 중첩된 사전 및 목록에 적용됩니다.
그것들을 디코딩하여 mgilson 코멘트로 비교합니다.
사전의 순서는 키와 값이 일치하는 한 중요하지 않습니다(Python에서는 사전 순서가 없습니다).
>>> {'a': 1, 'b': 2} == {'b': 2, 'a': 1}
True
그러나 목록에서는 순서가 중요합니다.목록을 정렬하면 문제가 해결됩니다.
>>> [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
위의 예는 질문의 JSON에 적용됩니다.일반적인 솔루션에 대해서는 Zero Piraeus의 답변을 참조하십시오.
네! jycm 사용가능합니다.
from jycm.helper import make_ignore_order_func
from jycm.jycm import YouchamaJsonDiffer
a = {
"errors": [
{"error": "invalid", "field": "email"},
{"error": "required", "field": "name"}
],
"success": False
}
b = {
"success": False,
"errors": [
{"error": "required", "field": "name"},
{"error": "invalid", "field": "email"}
]
}
ycm = YouchamaJsonDiffer(a, b, ignore_order_func=make_ignore_order_func([
"^errors",
]))
ycm.diff()
assert ycm.to_dict(no_pairs=True) == {} # aka no diff
보다 복잡한 예(심층 구조의 가치 변화)를 들 수 있다.
from jycm.helper import make_ignore_order_func
from jycm.jycm import YouchamaJsonDiffer
a = {
"errors": [
{"error": "invalid", "field": "email"},
{"error": "required", "field": "name"}
],
"success": True
}
b = {
"success": False,
"errors": [
{"error": "required", "field": "name-1"},
{"error": "invalid", "field": "email"}
]
}
ycm = YouchamaJsonDiffer(a, b, ignore_order_func=make_ignore_order_func([
"^errors",
]))
ycm.diff()
assert ycm.to_dict() == {
'just4vis:pairs': [
{'left': 'invalid', 'right': 'invalid', 'left_path': 'errors->[0]->error', 'right_path': 'errors->[1]->error'},
{'left': {'error': 'invalid', 'field': 'email'}, 'right': {'error': 'invalid', 'field': 'email'},
'left_path': 'errors->[0]', 'right_path': 'errors->[1]'},
{'left': 'email', 'right': 'email', 'left_path': 'errors->[0]->field', 'right_path': 'errors->[1]->field'},
{'left': {'error': 'invalid', 'field': 'email'}, 'right': {'error': 'invalid', 'field': 'email'},
'left_path': 'errors->[0]', 'right_path': 'errors->[1]'},
{'left': 'required', 'right': 'required', 'left_path': 'errors->[1]->error',
'right_path': 'errors->[0]->error'},
{'left': {'error': 'required', 'field': 'name'}, 'right': {'error': 'required', 'field': 'name-1'},
'left_path': 'errors->[1]', 'right_path': 'errors->[0]'},
{'left': 'name', 'right': 'name-1', 'left_path': 'errors->[1]->field', 'right_path': 'errors->[0]->field'},
{'left': {'error': 'required', 'field': 'name'}, 'right': {'error': 'required', 'field': 'name-1'},
'left_path': 'errors->[1]', 'right_path': 'errors->[0]'},
{'left': {'error': 'required', 'field': 'name'}, 'right': {'error': 'required', 'field': 'name-1'},
'left_path': 'errors->[1]', 'right_path': 'errors->[0]'}
],
'value_changes': [
{'left': 'name', 'right': 'name-1', 'left_path': 'errors->[1]->field', 'right_path': 'errors->[0]->field',
'old': 'name', 'new': 'name-1'},
{'left': True, 'right': False, 'left_path': 'success', 'right_path': 'success', 'old': True, 'new': False}
]
}
그 결과는 로 표현될 수 있다
다음 두 개의 딕트 'dictWithListsInValue' 및 'reordedDictWithReordedListsInValue'는 단순히 서로 정렬된 버전입니다.
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
잘못된 결과 즉, false를 내게 주었다.
그래서 다음과 같이 나만의 cutstom Object Comparator를 만들었습니다.
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
그 결과 예상한 대로 정확한 출력을 얻을 수 있었습니다!
논리는 매우 간단합니다.
개체가 '목록' 유형인 경우 첫 번째 목록의 각 항목을 찾을 때까지 두 번째 목록의 항목과 비교하고, 두 번째 목록을 살펴본 후에도 항목을 찾을 수 없으면 '찾았다'는 것은 = false입니다.'found' 값이 반환됩니다.
그렇지 않으면 비교할 객체가 'dict' 유형인 경우 두 객체의 모든 각 키에 대해 존재하는 값을 비교합니다.(재귀 비교 실행)
그렇지 않으면 단순히 obj1 == obj2로 호출합니다.디폴트로는 문자열과 숫자의 오브젝트에 대해서는 정상적으로 동작하며, 이러한 eq()에 대해서는 적절히 정의되어 있습니다.
(object1의 다음 항목이 object2에 이미 있는 항목과 자신을 비교하지 않도록 object2에 있는 항목을 삭제함으로써 알고리즘을 더욱 개선할 수 있습니다.)
독자적인 등가함수를 쓸 수 있습니다.
- dicts는 다음과 같은 경우 동일합니다. 1) 모든 키가 동일하고 2) 모든 값이 동일할 경우
- 리스트는 다음과 같은 경우 동일합니다.모든 항목이 동일하고 순서가 동일합니다.
- 「」의 경우는, .
a == b
.json 취 、 son 、 son because because::: 。dict
,list
해서 를 할 수if type(obj) == 'dict':
등등.
대략적인 예(테스트되지 않음):
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
2개의 JSON 오브젝트(보통 참조와 타겟이 있다)를 디버깅하고 싶은 경우는, 다음의 솔루션을 사용할 수 있습니다.타깃에서 참조까지의 서로 다른 경로 또는 일치하지 않는 경로의 목록이 표시됩니다.
level
옵션은 조사할 깊이를 선택하는 데 사용됩니다.
show_variables
옵션을 켜서 관련 변수를 표시할 수 있습니다.
def compareJson(example_json, target_json, level=-1, show_variables=False):
_different_variables = _parseJSON(example_json, target_json, level=level, show_variables=show_variables)
return len(_different_variables) == 0, _different_variables
def _parseJSON(reference, target, path=[], level=-1, show_variables=False):
if level > 0 and len(path) == level:
return []
_different_variables = list()
# the case that the inputs is a dict (i.e. json dict)
if isinstance(reference, dict):
for _key in reference:
_path = path+[_key]
try:
_different_variables += _parseJSON(reference[_key], target[_key], _path, level, show_variables)
except KeyError:
_record = ''.join(['[%s]'%str(p) for p in _path])
if show_variables:
_record += ': %s <--> MISSING!!'%str(reference[_key])
_different_variables.append(_record)
# the case that the inputs is a list/tuple
elif isinstance(reference, list) or isinstance(reference, tuple):
for index, v in enumerate(reference):
_path = path+[index]
try:
_target_v = target[index]
_different_variables += _parseJSON(v, _target_v, _path, level, show_variables)
except IndexError:
_record = ''.join(['[%s]'%str(p) for p in _path])
if show_variables:
_record += ': %s <--> MISSING!!'%str(v)
_different_variables.append(_record)
# the actual comparison about the value, if they are not the same, record it
elif reference != target:
_record = ''.join(['[%s]'%str(p) for p in path])
if show_variables:
_record += ': %s <--> %s'%(str(reference), str(target))
_different_variables.append(_record)
return _different_variables
import json
#API response sample
# some JSON:
x = '{ "name":"John", "age":30, "city":"New York"}'
# parse x json to Python dictionary:
y = json.loads(x)
#access Python dictionary
print(y["age"])
# expected json as dictionary
thisdict = { "name":"John", "age":30, "city":"New York"}
print(thisdict)
# access Python dictionary
print(thisdict["age"])
# Compare Two access Python dictionary
if thisdict == y:
print ("dict1 is equal to dict2")
else:
print ("dict1 is not equal to dict2")
KnoDL을 사용하면 매핑 필드 없이 데이터를 일치시킬 수 있습니다.
언급URL : https://stackoverflow.com/questions/25851183/how-to-compare-two-json-objects-with-the-same-elements-in-a-different-order-equa
'programing' 카테고리의 다른 글
여러 JSON 레코드를 Panda 데이터 프레임으로 읽기 (0) | 2023.03.16 |
---|---|
react를 사용하여 HTML5 데이터 속성을 동적으로 설정하려면 어떻게 해야 합니까? (0) | 2023.03.16 |
단순한 jQuery, PHP 및 JSONP의 예? (0) | 2023.03.16 |
Wordpress 업데이트 mysql 테이블 (0) | 2023.03.16 |
지정되지 않은 경우 암시되는 액세스 수식자는 무엇입니까? (0) | 2023.03.16 |