1. ホーム
  2. python

[解決済み] 異なる順序で同じ要素を持つ2つのJSONオブジェクトを等しく比較するには?

2022-07-09 17:17:57

質問

Pythonで2つのJSONオブジェクトが等しいかどうか、リストの順番を無視してテストするにはどうすればよいですか?

例えば...

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" のリストの順序が異なっていても、等しく比較されます。

どのように解決するのですか?

同じ要素を持つが順序が異なる 2 つのオブジェクトを等しく比較したい場合、明らかに行うべきことは、ソートされたコピーを比較することです。たとえば、JSON 文字列で表される辞書の場合 ab :

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" の項目は同じ要素を異なる順番で並べたリストだからです。 sorted() は反復可能の "top" のレベル以外では何もソートしようとしません。

それを修正するために 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

この関数を ab の場合、結果は同じように比較されます。

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