programing

같은 요소를 가진 두 JSON 개체를 다른 순서로 비교하려면 어떻게 해야 합니까?

css3 2023. 3. 16. 21:41

같은 요소를 가진 두 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