設定用のJSONで同じキーが二度現れたとき、どちらの値を使うかを暗黙に決めたくない場合があります。Pythonの標準jsonデコーダーは、既定では同じ名前の最後の値を残します。辞書に変換したあとで調べても、上書きされた値は見えません。
そこで、object_pairs_hookに渡されるキーと値のペアを確認し、同じオブジェクト内に重複があれば例外にする小さな関数を試しました。特定のAPIが重複を返したという報告ではなく、合成した入力だけを使ったローカル実験です。
辞書を作る前に重複を拒否する
import json
def unique_object(pairs):
result = {}
for key, value in pairs:
if key in result:
raise ValueError("duplicate object key")
result[key] = value
return result
def load_unique(text):
return json.loads(text, object_pairs_hook=unique_object)
cases = [
('{"timeout": 5}', {"timeout": 5}),
('{"timeout": 5, "timeout": 10}', "reject"),
('{"outer": {"timeout": 5, "timeout": 10}}', "reject"),
('[{"id": 1}, {"id": 2}]', [{"id": 1}, {"id": 2}]),
('{"id": 1, "ID": 2}', {"id": 1, "ID": 2}),
(r'{"x": 1, "\u0078": 2}', "reject"),
]
for text, expected in cases:
try:
actual = load_unique(text)
except ValueError:
actual = "reject"
assert actual == expected, (text, actual)
print("6 local duplicate-key cases passed")
2026年11月6日、Python 3.9.6で実行し、6 local duplicate-key cases passedを確認しました。外部APIやモデルは呼び出していません。
六つの入力で確認したこと
- 通常のオブジェクトは、そのまま読み込めます。
- 同一オブジェクトの
timeoutが重複した場合は拒否します。 - 入れ子になったオブジェクト内の重複も拒否します。
- 配列中の別々のオブジェクトが、それぞれ
idを持つことは許可します。 -
idとIDは異なる名前として扱います。 -
xとUnicodeエスケープの\u0078は、デコード後に同じ名前になるので拒否します。
このため、JSON文字列を検索して同じ文字列が二度あるか数える方法とは異なります。オブジェクトの境界と、デコードされたキー名で判定できます。
この関数が保証しないこと
これはアプリケーション側の重複拒否ポリシーです。JSON全般の完全な検証器でも、認可の仕組みでもありません。必要なフィールド、値の型、数値の範囲、非有限数、入力サイズや深さの制限は別に設計します。重複がないだけで、その入力を実行してよいとは判断しません。
既存の辞書からJSONを作ったテストでは、元の重複がすでに失われていることがあります。今回のように、受け取るJSONテキスト自体をテストデータにすると、この分岐を確認できます。
RouterBaseチームの技術メモです。AIの支援で作成し、掲載コードは上記のローカル環境で実行確認しました。参考:Python公式文書の重複名の扱い、object_pairs_hook。