Konboi Note

PythonでJSONオブジェクトのフィールドを順不同で比較する

· Konboi

はじめに

担当しているプロダクトではPython製のコマンドラインツールを提供しています。
サーバーへのリクエストのフォーマット(JSON)が正しい形式になっているかをテストしています。

ただテストのチェック用のデータのJSONのフィールドの順番も全く同じにするのは手間がかかります。ある程度簡単にデータを作成したいです。
そこでフィールドの順番が異なっていてもデータの中身が同じであればOKとするテスト用のユーティリーメソッドとしてassert_json_orderless_equal(a, b) を用意しています。

このメソッドの中身はこちらで示されているものをほぼ使っていました。

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

ただし、この処理だとdict型のfieldの値がNoneとそれ以外の場合で比較しようとするときにうまく比較できない事象に遭遇しました。

e.g.)

ordered({"array": [
    {"name": "a", "data": {"value": 1}},
    {"data": None, "name": "b"},
    {"name": "c", "data": {"value": 2}}
], "b": 2},
{"b": 2, "array": [
    {"data": None, "name": "b"},
    {"data": {"value": 2}, "name": "c"},
    {"data": {"value": 1}, "name": "a"},
]})

Noneも考慮した実装

そこでsortedの第二引数であるkeyにNoneの扱いをどうするか明示的に指定することで解決しました

def ordered(obj):
    if isinstance(obj, dict):
        return sorted([(k, ordered(v)) for k, v in obj.items()], key=lambda item: (item[1] is None, item))
    if isinstance(obj, list):
        return sorted(ordered(x) for x in obj)
    else:
        return obj

同様の問題で困っている方がいればご活用ください