Skip to content

JSON to CSV

Flatten an array of objects to a CSV with configurable nesting.

Live
Input

What this tool does

Flatten an array of JSON objects into a strict RFC 4180 CSV file — with configurable handling for nested objects (dot / bracket / stringify), a custom delimiter, and an optional header row. Useful when you have a JSON API response and need to dump it into a spreadsheet, a BI tool that only ingests CSV, or a one-shot COPY FROM into Postgres.

Output is quoted strictly per RFC 4180: cells containing the delimiter, double-quotes, or newlines are wrapped in "…" with internal quotes doubled. The first row is the header (object keys in first-seen order); subsequent rows are one object each. Nothing is uploaded — the conversion runs in your browser via a streaming serializer.

How to use it

Paste a JSON array of objects (or load the example), pick a delimiter and a nested-key strategy (dot / bracket / stringify), and read the CSV on the right. The header row is the union of all keys observed in the first-seen order; cells absent from a particular object render as an empty quoted string.

Input: [{"id":1,"name":"ada","email":"ada@example.com"},{"id":2,"name":"linus","email":"linus@example.com"}]

Output (CSV):

id,name,email
1,ada,ada@example.com
2,linus,linus@example.com

Limits and edge cases

  • CSV needs an array (or a single object, which gets wrapped in a one-row table). Primitive arrays like [1, 2, 3] produce a single-column value table; bare strings / numbers / booleans as the root JSON are rejected with a clear error.
  • Nested objects are flattened via the strategy you pick. Dot (user.address.city) is the default and matches most BI tools’ expectations. Bracket notation (user[address][city]) matches PHP / Rails query strings. Stringify keeps the nested value as a JSON-encoded cell — useful when the downstream tool can parse JSON cells (BigQuery, Snowflake, DuckDB) and you want to preserve structure.
  • Arrays nested inside objects are always JSON-stringified into a single cell — CSV’s flat row-and-column model cannot natively represent [1, [2, 3], 4] in a single column.
  • Output is UTF-8 without a byte-order mark by default. Excel for Windows requires the BOM to render UTF-8 correctly (otherwise CJK characters and emoji become mojibake); toggle UTF-8 BOM on in the options pane if your audience is Excel.
  • Empty cells render as "" (two quote characters) when the column appears in some rows but not others. Missing values are not the literal string null — Postgres’ COPY FROM treats unquoted empties as NULL, which is usually what you want.
  • For deeper coverage of flattening strategies and Excel quirks, see JSON to CSV: flattening nested data. For invalid JSON input, run JSON Repair first.

Frequently asked questions

Why does Excel show my CSV as mojibake (亂碼) when I open it?
Excel for Windows assumes ANSI / system-default encoding unless the CSV starts with a UTF-8 BOM (EF BB BF). CJK characters and emoji come out as mojibake without it. Toggle 'UTF-8 BOM' on in the options pane and re-export; Excel will then render the file correctly. macOS Excel and Google Sheets handle BOM-less UTF-8 fine.
How are values containing commas, quotes, or newlines escaped?
Strictly per RFC 4180: any cell that contains the delimiter, a double-quote, or a CR/LF is wrapped in double-quotes, and internal double-quotes are doubled (`he said "hi"` → `"he said ""hi"""`). Cells without specials are emitted unquoted. The output is compatible with every CSV parser that follows RFC 4180 (which is most of them in 2026).
What if my JSON objects have inconsistent keys across rows?
The header row is the union of all keys observed, in first-seen order. Rows that lack a particular key render that cell as an empty quoted string (`""`). This matches the behaviour of `pandas.json_normalize()` and DuckDB's `read_json()` — sparse columns are kept, not silently dropped.
Can I use a tab delimiter to make TSV?
Yes — switch the delimiter to Tab in the options pane. The output is technically TSV (RFC 4180 wasn't written for tab-separated values), so cells containing actual tabs get quoted; that's not what every TSV consumer expects, but it round-trips through any RFC 4180-style parser. For canonical TSV (no quoting, tabs in cells are an error), do the JSON → CSV step then pipe through `tr ',' '\t'`.
How do I get empty cells to render as NULL for Postgres COPY?
Leave the cell as missing in the input JSON (omit the key) and pick the default flatten strategy. The output emits a bare empty cell (no quotes) for missing keys, which Postgres `COPY FROM` treats as NULL. Explicit `null` values in the JSON render as the literal string `null` instead — quote them or pre-process if you need them to import as NULL.
Why don't you have a CSV → JSON direction?
CSV doesn't carry enough type information to round-trip back to typed JSON without guesses. The cell `1` is a number or a string of length 1; `true` is a boolean or a string. A reasonable converter has to ask you per-column, which means tabular UI, which means a separate tool. Out of scope for the converter; a CSV-to-JSON tool with column type inference is on the roadmap.

Content reviewed by