Flatten Nested JSON Into CSV Columns
Choose “Flatten objects to dot paths.” A nested value such as profile.city becomes a profile.city column, and keys found in later records join the same first-seen header. Arrays stay JSON text in one cell because expanding an arbitrary list would make the number and meaning of columns depend on each row.
A JSON object, an array of objects, or an array of arrays up to 20 MB. It is parsed only in this tab.
Nested object keys become stable dot paths, so address.city and address.country remain visibly related.
Arrays stay serialised in one quoted cell rather than turning item positions into an unbounded set of columns.
A literal key containing a dot is escaped with a backslash so it cannot collide with a nested path.
How it works
How it is done
- Decode the file as UTF-8, discard a leading UTF-8 marker when present, and parse it with the browser's built-in JSON parser. Empty or syntactically invalid input is refused with the parser's reason.
- Decide the row model from the top level. One object is one row; an array of objects is many named rows; an array of arrays is many positional rows. Mixed row shapes are refused rather than guessed.
- For object rows, collect the union of keys in first-seen order so a field that appears only in a later record still gets one stable column and earlier rows get an empty cell there.
- When flattening is selected, walk nested objects into escaped dot paths such as profile.city. Keep arrays as JSON in one cell because their length is data, not a dependable set of columns.
- Render null or a missing field as an empty cell, booleans and numbers as JSON values, strings as text, and any remaining object or array as compact JSON.
- When formula protection is on, prefix string cells and headings whose first meaningful character is =, +, - or @ with an apostrophe. Real JSON numbers, including negative numbers, remain numeric.
- Double every quote inside a field and wrap a field in quotes when it contains the chosen separator, a quote or a line break. End every row with CRLF as the CSV convention specifies.
- Add the optional three-byte UTF-8 marker, encode the complete text, and offer .csv for comma or semicolon output or .tsv for tabs. Nothing is sent away at any point.
What it assumes
- Supported top-level shapes are one object, an array containing only objects, or an array containing only arrays. A primitive, an empty array or a mixture of row shapes has no unambiguous table and is refused.
- Object columns are the union of keys across every row, ordered by the first appearance of each key. Missing and null are both empty CSV cells because CSV has no distinct null value.
- Flattening descends through objects only. Arrays remain compact JSON in one cell; expanding list positions would let one unusually long row create thousands of columns and would pretend position means the same thing in every record.
- A literal dot or backslash inside a JSON key is escaped with a backslash before path segments are joined, so a key named profile.city cannot collide with the nested path profile then city.
- Formula protection applies only to JSON strings and column names. A typed JSON number such as -2 stays -2; the string "-2" is prefixed because a spreadsheet decides cell type from the CSV text.
- The UTF-8 marker is enabled by default for older Excel versions. It adds the three bytes EF BB BF and no row or column data; other spreadsheet and database tools normally tolerate it.
- Comma and semicolon output use the .csv extension. A tab separator uses .tsv and the text/tab-separated-values MIME type rather than labelling tabular data as comma-separated.
- Input is capped at 20 MB, at 500,000 rows and 5,000 columns. The rendered text is capped at 64 million characters so one expansion cannot exhaust the tab's memory.
Common questions
Why are JSON arrays not expanded into numbered CSV columns?
Array length is data, not schema. Expanding positions would let one unusually long record add thousands of columns and would make item zero in different rows look like the same field even when it is not.
How is a dot inside an original JSON key represented?
A literal dot or backslash in a key is escaped with a backslash before path segments are joined, so the key profile.city and the nested path profile then city remain distinct headings.
Sources
The full method, worked example and every assumption behind this figure are on JSON to CSV Converter.