The direct answer

Reject ambiguous member names and unacceptable number spellings before ordinary decoding discards them. Use one explicit raw-input contract, preserve decoded pairs and numeric kinds until admission, and carry exact values through the downstream handoff. Schema and authorization checks still follow.

Preserve the evidence before schema validation

A model proposes a small quote line. One component reads an object, another validates its schema, and a third calculates a total. If the first decoder silently discards a repeated member or changes a number's representation, later checks cannot inspect the original choice.

The structured-output guide covers schema, authorization and business constraints. This guide defines an earlier raw-input boundary. It does not replace those checks or turn a well-decoded proposal into permission to act.

Choose one documented decoder contract

The official Python json documentation opened here identifies release 3.15.2. Its defaults accept repeated names using the last value, and accept nonstandard nonfinite numbers. The decoder's strict=True concerns control characters inside strings; it does not establish this entire application policy.

For the fictional receiver, accept at most eight KiB of decompressed body bytes, require strict UTF-8 without a byte-order mark, then use json.loads on that text as one complete document. Require an object root. These are proposed application restrictions, not Python defaults or measured safe ceilings. An upstream middleware must not decode the body first and hand this boundary an ordinary dictionary.

Reject decoded-name duplicates at every object level

RFC 8259 recommends unique object names and describes inconsistent receiver behavior for duplicates. It also explains comparison after escapes are decoded. Our policy rejects any repeated decoded name within the same object; it neither chooses a winner nor merges values.

The selected Python API offers object_pairs_hook an ordered list of pairs for every decoded object, replacing the usual dictionary result. Use that opportunity to check uniqueness before constructing the mapping, including objects inside arrays. An ordinary object_hook receiving a dictionary is too late to recover discarded pairs.

The proposed shape is exactly one top-level line object containing quantity and price_text. Check those permitted names at their structural locations. Compare decoded names exactly; do not normalize, trim or case-fold them into accepted fields. This concerns JSON member identity, distinct from duplicate business identifiers or Unicode display-label comparison.

Keep number spelling and value kind until admission

In this profile, every JSON number must be a nonnegative integer token matching the whole ASCII grammar 0|[1-9][0-9]{0,5}: zero through 999999. The six-digit limit is deliberate. Negative zero, fractions and exponent spellings are rejected even when some spellings could describe the same mathematical integer.

Use parse_int to inspect the supplied number string and return an internal tagged number record after the lexical check. Preserve its original lexeme and number kind until field validation; do not return an indistinguishable ordinary string. Configure parse_float and parse_constant to reject their inputs for this profile. Python documents these string-valued callback inputs; no callback implementation runs here.

Only after checking that quantity is the admitted numeric kind should the receiver convert it to an integer for schema and business processing. A boolean or quoted numeric string is not a quantity. The internal tag must be a trusted runtime type, not a JSON object whose fields a producer can imitate. Custom integer handling needs its own bound rather than relying on a default conversion limit.

Use an original raw-input decision worksheet

Start with the harmless positive proposal below. The strings and snippets are inert worksheet notation, never supplied to a parser in this article.

{"line":{"quantity":12,"price_text":"1.25"}}
Original decoding and handoff matrix — PROPOSED_UNRUN
Proposed input conditionExpected decisionInformation preserved
Positive line aboveProceed to schema/business checksQuantity token 12; price string 1.25
Two literal quantity members in lineReject whole documentPair list reveals both occurrences
Nested line uses "quantity" and "\u0071uantity"Reject decoded-name collisionDifferent source spellings, same decoded name
Quantity token 12.0 or 12e0Reject lexical formDo not coerce into accepted 12
Quantity token -0 or 1000000Reject profile grammar or lengthNo clipping or silent repair
Quantity is true or "12"Reject wrong value kindNo boolean/string-to-integer coercion
NaN or Infinity in a numeric positionReject nonstandard literalNo nonfinite value reaches schema
price_text is "01.25" or "1.250"Reject decimal-string contractNo formatting repair
Receiver gets only an already-decoded objectCannot attest raw decoding contractRevisit intake; do not invent missing evidence

Specify the exact downstream representation

The separate price_text string must match the whole ASCII grammar (0|[1-9][0-9]{0,3})\.[0-9]{2}. This permits 0.00 through 9999.99 with exactly two decimal places. Convert validated digits directly to integer minor units: 1.25 becomes 125; twelve units produce 1500 minor units. Do not route that conversion through binary floating point.

Keep price as the canonical string when serializing the accepted proposal onward, and quantity as its bounded integer. Require the downstream consumer to preserve that contract rather than converting price to an approximate number. RFC 8259 notes numeric range and precision interoperability limits; successful schema validation alone does not prove this handoff. A consumer with an incompatible representation stays unsupported until reviewed.

Keep resource and signature limits outside the hook claim

Member-pair callbacks receive already-decoded structures, and numeric callbacks receive allocated token strings. They do not establish pre-allocation nesting, memory or CPU bounds. The byte ceiling limits input volume, not all work. A real deployment needs separately reviewed resource enforcement and implementation-specific depth handling; none is demonstrated here.

Keep necessary raw bytes within the authorized intake boundary until protocol checks are complete. Never parse and reserialize as a shortcut for verifying a callback signature: follow the actual protocol's signed representation. Do not log complete rejected documents to explain a decoding error.

All acceptance cases are PROPOSED_UNRUN. No Python json import, parser execution, schema validation or numeric-conversion experiment was performed for these proposed cases. No installation, API or model call occurred. Boundary remains a prelaunch commercial model router, not a shipped strict-JSON decoder. Its eight mock policy checks provide different evidence.

Primary sources

Official references supporting the guidance. Examples in this guide are synthetic and are not reports of a production incident.

  1. Python json documentation (observed release 3.15.2)
  2. RFC 8259 — The JSON Data Interchange Format