Guides / Container depth
Check JSON Container Depth Before CSV Conversion
Define the root container as depth 1, scan for object and array boundaries outside strings, and reject over-limit input before JSON decoding. Then validate the complete document before exporting CSV. Brackets inside a quoted string do not add nesting. A depth check performed only after parsing cannot protect that earlier parsing step.
Count containers, not field names or punctuation
For this recipe, a root array has depth 1, each record object has depth 2, and its payload object has depth 3. A payload array inside that object reaches depth 4. Root scalar values have container depth 0, although the exporter below accepts only a record array. This convention is explicit so an at-limit example is reproducible; it is not a universal convention used by every converter.
[ // root array: 1
{ // record object: 2
"id": "D1",
"payload": { // payload object: 3
"note": "[ { } ]" // string punctuation: no extra depth
}
}
]This annotated sketch includes comments for explanation and is not JSON input. The downloadable fixture below is valid JSON. Our gate uses a stack for container delimiters and two string states: inside a string and escaping its next character. An escaped quote must not end the string; an escaped backslash must not leave the following real quote escaped. The executable fixture includes both conditions.
One depth gate is not a JSON validator
A scanner can find excess nesting without building nested objects. It cannot establish all JSON grammar rules. For example, balanced brackets around an object with a trailing comma still pass the boundary scan and must fail in the JSON decoder. The correct order is bounded byte read, strict UTF-8 decode, depth scan, complete JSON validation, record contract validation, then output creation.
RFC 8259, section 9 allows implementations to set a nesting-depth limit. It does not prescribe the recipe's value of 3. Python's JSON reference describes decoder behavior and warns about CPU and memory use with untrusted data. This is a local Python recipe, not a measured depth limit of the site's browser converter.
Original at-limit fixture and exact CSV read-back
[{"id":"D1","payload":{"note":"brackets [ { } ] are text"}},{"id":"D2","payload":{"note":"quote \" then [ and backslash \\"}}]
On 2026-10-13, Python 3.12.14 executed the downloadable exporter with a maximum depth of 3. Both original records passed; the CSV was read back and its nested payloads and strings matched the JSON exactly. The same input failed with a limit of 2 before any CSV output was created. These values are our recipe policy and fixture results, not a benchmark of every JSON implementation.
python depth-to-csv.py depth-input.json new-output.csv --max-depth 3
python depth-verify.pyDownload all files into one directory and run with Python 3.12 or later. The checker reads the supplied measured CSV; edit its filename if checking a newly produced file. The exporter keeps id as a string and stores the nested payload as JSON text in one payload_json cell. It does not flatten or discard nested fields. Loading that cell as JSON reconstructs the payload. A spreadsheet display is not the round-trip check.
Executed rejection cases
| Fixture | Depth policy | Result |
|---|---|---|
| at-limit | 3 | Created; exact read-back |
| below-limit | 2 | ValueError: container depth exceeds 2 |
| one-level-deeper | 3 | ValueError: container depth exceeds 3 |
| deep-before-parse | 3 | ValueError: container depth exceeds 3 |
| balanced-invalid-json | 3 | JSONDecodeError: Expecting property name enclosed in double quotes: line 1 column 35 (char 34) |
| wrong-schema | 3 | ValueError: demo requires string leaves |
| escaped-backslashes | 3 | Created; exact read-back |
The generated 10,000-array fixture is rejected by the scanner when it opens the fourth container, before json.loads runs. This demonstrates order of operations for that input, not general resistance to all malicious documents. The balanced trailing-comma case reaches the decoder and fails there. Numeric payload leaves fail the demo contract; this deliberately narrow example accepts string leaves, arrays and objects rather than promising precision-preserving conversion for arbitrary numbers.
The exporter rejects duplicate object keys, nonstandard constants and strings that cannot be encoded as UTF-8. It reads no more than 1,000,001 input bytes and rejects anything above its 1,000,000-byte policy. The adjustable depth policy accepts values 1 through 64. Neither setting is a universal safe threshold. Exclusive file creation refuses to overwrite an existing CSV. Invalid input is rejected before output creation; a later disk failure can still leave partial output.
What a depth gate does not bound
The scan still holds the bounded input text in memory, and decoding creates additional objects. A shallow document can contain many fields or very long strings. Container depth alone therefore does not guarantee total memory use, CPU time or output size. For unknown input, use additional resource controls appropriate to the environment. CSV quoting also does not neutralize spreadsheet formulas.
If the permitted data is legitimately deeper, agree on the expected structure before changing the policy. Do not silently truncate nested values to make a CSV appear successful. For choosing a record array rather than treating every nested array as rows, see the guide collection; for completion checks, use row and field verification.
The same input contract in three images
For an ordinary JSON document you can inspect, open the JSON to CSV converter. This guide's downloadable recipe is a separate reproducible workflow.