Batu Lab NotesPractical developer guides

Reject duplicate JSON object names with object_pairs_hook

By Batu ยท English technical notes

Also published in our primary archive.

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'