Reject duplicate JSON object names with object_pairs_hook
Reject duplicate JSON object names with object_pairs_hook
To reject duplicate JSON object names in Python, pass a checking function as object_pairs_hook to json.loads(). The hook receives the ordered key/value pairs before they become a dictionary, which is the point at which duplicate information is still available.
The fixture below is exactly {"role":"reader","role":"admin"}. Default decoding produces {'role': 'admin'}: Python's JSON decoder accepts repeated names and uses the final value. That behavior can be unsuitable when each name is meant to be a single declaration. The assertion records the default result rather than treating it as a parser error.
reject_duplicates builds its own result dictionary. Before inserting each pair, it checks whether the name has already appeared. The second role therefore raises a deliberately narrow ValueError; it does not validate value types, required fields, or nested application rules. The printed diagnostic is deterministic because it uses the duplicated input key rather than a whole dictionary representation.
object_pairs_hook was added in Python 3.1 and takes priority over object_hook when both are supplied. This check applies to every JSON object decoded by that call, including nested objects, so a format with different rules by nesting level needs a context-aware design. Python documents both the ordered-pairs hook and its precedence in the json documentation.
AI assistance disclosure: This article was drafted with AI assistance and verified with the synthetic fixture shown.
Example
import json
source = '{"role":"reader","role":"admin"}'
default_value = json.loads(source)
assert default_value == {"role": "admin"}
print("default:", default_value)
def reject_duplicates(pairs):
result = {}
for key, value in pairs:
if key in result:
raise ValueError("duplicate key {!r}".format(key))
result[key] = value
return result
try:
json.loads(source, object_pairs_hook=reject_duplicates)
except ValueError as error:
print("validated:", error)
Expected output:
default: {'role': 'admin'}
validated: duplicate key 'role'