Python 如何以不同的顺序比较具有相同元素的两个 JSON 对象相等?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/25851183/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 23:44:32  来源:igfitidea点击:

How to compare two JSON objects with the same elements in a different order equal?

pythonjsondjangocomparison

提问by

How can I test whether two JSON objects are equal in python, disregarding the order of lists?

如何在 python 中测试两个 JSON 对象是否相等,而不管列表的顺序?

For example ...

例如 ...

JSON document a:

JSON 文档a

{
    "errors": [
        {"error": "invalid", "field": "email"},
        {"error": "required", "field": "name"}
    ],
    "success": false
}

JSON document b:

JSON 文档b

{
    "success": false,
    "errors": [
        {"error": "required", "field": "name"},
        {"error": "invalid", "field": "email"}
    ]
}

aand bshould compare equal, even though the order of the "errors"lists are different.

a并且b应该比较相等,即使"errors"列表的顺序不同。

采纳答案by Zero Piraeus

If you want two objects with the same elements but in a different order to compare equal, then the obvious thing to do is compare sorted copies of them - for instance, for the dictionaries represented by your JSON strings aand b:

如果您希望具有相同元素但以不同顺序比较的两个对象相等,那么显而易见的事情就是比较它们的排序副本 - 例如,对于由您的 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

... but that doesn't work, because in each case, the "errors"item of the top-level dict is a list with the same elements in a different order, and sorted()doesn't try to sort anything except the "top" level of an iterable.

...但这不起作用,因为在每种情况下,"errors"顶级 dict 的项目是一个列表,其中具有不同顺序的相同元素,并且sorted()除了“顶级”级别之外不会尝试对任何内容进行排序一个可迭代的。

To fix that, we can define an orderedfunction which will recursively sort any lists it finds (and convert dictionaries to lists of (key, value)pairs so that they're orderable):

为了解决这个问题,我们可以定义一个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

If we apply this function to aand b, the results compare equal:

如果我们将此函数应用于aand b,则结果比较相等:

>>> ordered(a) == ordered(b)
True

回答by falsetru

Decode them and compare them as mgilson comment.

解码它们并将它们作为 mgilson 注释进行比较。

Order does not matter for dictionary as long as the keys, and values matches. (Dictionary has no order in Python)

只要键和值匹配,顺序对字典无关紧要。(字典在 Python 中没有顺序)

>>> {'a': 1, 'b': 2} == {'b': 2, 'a': 1}
True

But order is important in list; sorting will solve the problem for the lists.

但顺序在列表中很重要;排序将解决列表的问题。

>>> [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

Above example will work for the JSON in the question. For general solution, see Zero Piraeus's answer.

上面的示例适用于问题中的 JSON。有关一般解决方案,请参阅 Zero Piraeus 的回答。

回答by stpk

Another way could be to use json.dumps(X, sort_keys=True)option:

另一种方法是使用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

This works for nested dictionaries and lists.

这适用于嵌套字典和列表。

回答by Gordon Bean

You can write your own equals function:

您可以编写自己的 equals 函数:

  • dicts are equal if: 1) all keys are equal, 2) all values are equal
  • lists are equal if: all items are equal and in the same order
  • primitives are equal if a == b
  • 如果满足以下条件,则 dicts 相等:1) 所有键都相等,2) 所有值都相等
  • 列表相等,如果:所有项目都相等且顺序相同
  • 如果基元相等 a == b

Because you're dealing with json, you'll have standard python types: dict, list, etc., so you can do hard type checking if type(obj) == 'dict':, etc.

因为您处理JSON,你就会有标准的Python类型:dictlist等等,所以你可以做硬类型检查if type(obj) == 'dict':,等等。

Rough example (not tested):

粗略示例(未测试):

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

回答by NiksVij

For the following two dicts 'dictWithListsInValue' and 'reorderedDictWithReorderedListsInValue' which are simply reordered versions of each other

对于以下两个字典 'dictWithListsInValue' 和 'reorderedDictWithReorderedListsInValue',它们只是彼此重新排序的版本

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

gave me wrong result i.e. false .

给了我错误的结果,即 false 。

So I created my own cutstom ObjectComparator like this:

所以我像这样创建了自己的 cuttom ObjectComparator:

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

which gave me the correct expected output!

这给了我正确的预期输出!

Logic is pretty simple:

逻辑很简单:

If the objects are of type 'list' then compare each item of the first list with the items of the second list until found , and if the item is not found after going through the second list , then 'found' would be = false. 'found' value is returned

如果对象是 'list' 类型,那么将第一个列表的每个项目与第二个列表的项目进行比较,直到找到,如果在通过第二个列表后没有找到该项目,则 'found' 将为 = false。'找到' 值被返回

Else if the objects to be compared are of type 'dict' then compare the values present for all the respective keys in both the objects. (Recursive comparison is performed)

否则,如果要比较的对象是“dict”类型,则比较两个对象中所有相应键的值。(进行递归比较)

Else simply call obj1 == obj2 . It by default works fine for the object of strings and numbers and for those eq() is defined appropriately .

否则只需调用 obj1 == obj2 。默认情况下,它适用于字符串和数字对象,并且对于那些eq() 被适当定义。

(Note that the algorithm can further be improved by removing the items found in object2, so that the next item of object1 would not compare itself with the items already found in the object2)

(请注意,可以通过删除在 object2 中找到的项目来进一步改进算法,以便 object1 的下一个项目不会与在 object2 中已找到的项目进行比较)