Separate parsing from acceptance
A decoder knows syntax and conversions; it does not know your service rules. JSON producing a dictionary may contain repeated names already reduced to the last value. If the contract forbids repetition, inspect pairs during decoding, for example through object_pairs_hook, before losing that information. Then validate required fields, types, bounds, and relationships between values. bool subclasses int, so isinstance(value, int) accepts True; make clear whether that is permitted. Also define input limits before allocating the document. Rejecting a million records after loading them does not bound initial cost.
Preserve numeric meaning
For a decimal amount, parse_float=Decimal allows construction from the textual token without first passing through float. This does not itself define currency, scale, rounding, or limits. Check finiteness: Decimal also represents NaN and infinities. The json decoder accepts certain nonfinite constants by default; parse_constant can reject them. However, amount="NaN" is a string and does not pass through that handling as a numeric constant. On output, allow_nan=False handles special float values during serialization. Do not confuse these options or assume valid conversion makes every value eligible for financial calculation.
Agree on the complete representation
Producer and consumer need the same format contract. ensure_ascii may represent Évora with an escape without removing the accent; that differs from converting a Decimal into a string through default=str, which changes the delivered JSON type. JSON object keys are text: Python identities 7 and "7" need a collision-free model if they represent different entities. Finally, two consecutive dump calls do not automatically create an array or a multiple-document protocol. Choose one document or framing explicitly supported by both sides. Test the round trip and received types, not merely file appearance.
Consume without losing the delivery boundary
An iterator retains consumption state. Counting rows with sum may exhaust the exact object intended for later processing. If two passes are required, choose between recreating the source, retaining bounded data, or another strategy with known costs. To pair IDs and amounts, ordinary zip stops at the shortest input; strict=True detects unequal lengths when iteration reaches the mismatch. Because evaluation is incremental, earlier loop operations may already have produced effects. Validate fully before publication when the contract requires a complete batch, using staging or a transactional protocol compatible with volume and consumer.
Manage retention and resource lifetime
A lazy interface guarantees neither constant memory nor open resources. With tee, a far-behind consumer may require many values to be retained; the resulting iterators are not an automatic solution for concurrent thread consumption either. A generator expression returned from inside with open may attempt reading only after the file closes. Define who opens, consumes, and closes. Cleanup must preserve failure: a return in finally may mask an exception and turn failed reading into empty success. Exercises should observe values, exceptions, and effects already produced, including early termination and partial data.
import json
def unique_names(pairs):
result = {}
for name, value in pairs:
if name in result:
raise ValueError("repeated field: " + name)
result[name] = value
return result
payload = '{"batch":"B-42","limit":12,"limit":24}'
try:
json.loads(payload, object_pairs_hook=unique_names)
except ValueError as exc:
print(str(exc))Original exercise: the B-42 configuration object contains limit twice. The hook rejects the second name instead of silently choosing a value. This exercise does not validate a complete schema: separately add required fields, types, finiteness, and bounds before accepting configuration.
Common pitfalls
Treating loads as a schema validator; checking duplicates after dict; converting precision into float for convenience; reusing exhausted iterators; assuming strict performs rollback; hiding errors in finally.
Related topics: Small functions, clear contracts · Errors and files with context · Precision, external data, and time · Observable and repeatable automation
Validate before losing information and before publishing effects; types, limits, identity, and resource lifetime belong in the contract.
Reference: Python3.14: JSON encoder and decoder · Python 3.14; DR Python 2026.3