Reversible data workflow
JSON flattening: paths, collisions and reversible data
Flattening is a mapping convention, not part of the JSON standard. A useful export must explain its path grammar and refuse ambiguous data instead of silently overwriting a value.
Content updated: · Maintained by Hung Chi-Hsueh · corrections
Choose an explicit path grammar
The default bracket format accepts a JSON object at the root in both directions. Object keys are separated by dots: user.address.city. Array entries use canonical decimal brackets: users[0].name or matrix[0][1]. A numeric object key remains a property: numeric.0 is different from numeric[0]. Root arrays and primitives are rejected in this default format, not automatically wrapped. Flatten-only dot-index format also accepts root arrays, uses users.0.name and represents an empty root array at the empty path. Numeric object keys and array indexes are ambiguous in this format, so it cannot be unflattened.
Source object keys must be nonempty and cannot contain a dot, [ or ]. Otherwise a literal a.b key could collide with an a object containing b. The tool rejects that input rather than choosing a winner. Slash, tilde, Unicode and whitespace in keys remain literal. The exact segments __proto__, constructor and prototype are deliberately refused. No wildcard, JSONPath, custom delimiter or key escaping is provided.
RFC 6901 JSON Pointer is a separate standardized syntax with slash separators and ~ escaping. Use the Pointer explorer when you need to address arbitrary JSON keys, including empty or delimiter-containing keys; do not treat a flat-map key as a JSON Pointer.
Preserve meaning, reject invented structure
Flatten keeps null, booleans, strings, supported numbers, empty objects and empty arrays as leaves. An empty root object produces {} with zero fields. Unflatten accepts only those leaf values; a nonempty container inside a flat map is rejected. A path cannot be both a leaf and a parent, or use the same parent as both object and array.
Array indices must start at zero and be contiguous. items[1] alone is rejected rather than inventing items[0]:null. Leading zeroes, negative indices, malformed brackets and excessively large indices are rejected before allocating an array. Duplicate paths and a/a.b or a.0/a[0] conflicts block the complete operation; failed conversions have no exportable partial result.
For supported values in the default bracket format, flatten followed by unflatten preserves JSON meaning. It does not preserve indentation, object-key order or the original numeric spelling, such as 1e3 instead of 1000. The strict shared JSON parser rejects duplicate keys, unsafe integers, negative zero, unpaired Unicode surrogates and numeric tokens that change value on serialization. Keep exact identifiers and decimals as strings.
Bounded previews and local-only processing
Flatten input is limited to 1 MiB of UTF-8; Unflatten input allows 8 MiB so a bounded flattened export can be read back. Each path is limited to 4,096 UTF-8 bytes, documents to 100 levels and 50,000 nodes, and complete compact output to 8 MiB. Reconstructed depth, nodes and output size are checked too. Limits reject the conversion; they never silently truncate data.
Processing uses a dedicated local Worker with a five-second timeout. Cancel terminates conversion; editing the source, changing direction or leaving the mode invalidates pending file reads and old results. A file read may finish in the browser after cancellation, but its late result is ignored. Files are decoded strictly as UTF-8 without BOM; invalid bytes are not replaced.
The table shows 100 fields per page and at most 1,000 characters of each value. Copy, download and the expandable read-only JSON contain the complete result. Inputs and filenames are not uploaded, saved, placed in URLs or sent to tool analytics. A downloaded file can still contain sensitive data; local processing is not anonymization or secure deletion. Clipboard permissions and Worker availability depend on the browser; failures leave manual selection or a clear retry message.
{"user":{"name":"Ada"},"items":[null],"empty":{},"numeric":{"0":"zero"}}
→ {"user.name":"Ada","items[0]":null,"empty":{},"numeric.0":"zero"}
Rejected: {"a.b":1,"a":{"b":2}}
Rejected: {"items[1]":"missing index zero"}Data provenance