Require a JSON object instead of accepting a list
Require a JSON object instead of accepting a list
To require a JSON object instead of accidentally accepting a list, decode first and test type(value) is dict. JSON {} becomes a Python dictionary and [] becomes a Python list. Both values are iterable, so a legacy check based on iter(value) accepts both even though an API contract may require an object at the document root.
This example makes the boundary visible by running the same iterable-based check and the object-contract check for each JSON text. The empty object passes both checks. The empty array also passes the legacy check, but the corrected check rejects it because its exact decoded type is list. type(value) is dict is intentionally narrow: it is appropriate here because json.loads() returns built-in containers under its default decoder. If the contract should accept a custom mapping supplied through decoder hooks, use a deliberately chosen abstract mapping check instead.
The assertions show only these two top-level boundary cases and do not validate the contents of a non-empty object. Python's conversion table documents that a JSON object decodes to dict and a JSON array decodes to list; see the json decoder documentation.
No newer API is used; this example is compatible with Python 3. AI assistance disclosure: this article was drafted with AI assistance and uses synthetic in-memory JSON text.
import json
def legacy_accepts(value):
try:
iter(value)
except TypeError:
return False
return True
def accepts_object(value):
return type(value) is dict
cases = ["{}", "[]"]
results = []
for text in cases:
value = json.loads(text)
result = (text, type(value).__name__, legacy_accepts(value), accepts_object(value))
results.append(result)
assert results == [
("{}", "dict", True, True),
("[]", "list", True, False),
]
for text, kind, legacy, corrected in results:
print(text + " -> " + kind + "; iterable=" + str(legacy) + "; object=" + str(corrected))
{} -> dict; iterable=True; object=True
[] -> list; iterable=True; object=False