Dot notation, and how deep the flattening goes
JSON nests and CSV does not. That single mismatch is the whole difficulty of this conversion, and every tool that does it has to pick a policy for what happens to structure that a grid cannot represent.
The policy here is dot notation, applied recursively. A record like {"user":{"city":"NYC"}} produces a column headed user.city holding NYC. Nesting composes without limit, so {"a":{"b":{"c":1}}} becomes a column called a.b.c, and a profile object four levels deep produces four-segment headings rather than a cell reading [object Object].
One thing that is explicitly not treated as nesting is null. JavaScript famously reports null as an object type, and a converter that recursed into it would crash or emit nonsense, so it is handled as an ordinary scalar value and lands in its cell as an empty string.
The cost of the dot convention is ambiguity in one direction. Once a heading reads user.city, there is no way to know whether the source had a nested object or a top-level key with a dot in its name. That rarely matters for a report, and it matters a lot if you plan to convert the result back.
An array value stays in a single cell
The other shape a grid cannot hold is a list. Faced with "tags":["admin","user"], a converter can either spread it across numbered columns such as tags.0 and tags.1, or keep it whole in one cell. This tool keeps it whole and writes it as its JSON text, so the cell literally contains ["admin","user"].
Spreading was rejected on purpose. If arrays became columns, the width of your entire spreadsheet would be dictated by whichever single row happened to have the longest list, and every other row would carry trailing blanks. One user with fifteen tags would add fifteen columns that 99 percent of the file leaves empty.
Keeping the array intact means the column set depends only on the object keys, which is predictable, and the value is recoverable: running JSON.parse over that cell gives the original array back exactly. The trade is that you cannot filter on a single tag in a spreadsheet without splitting the text yourself first.
The header is a union, not the first row’s keys
Real exports are ragged. An API returns some records with an optional field and some without, a survey has questions that only appear on branching paths, and a database dump has columns added mid-history.
A naive converter reads the keys off the first object and assumes the rest match. When one does not, every value after the missing field shifts one column left, and the damage is invisible until somebody scrolls to row 40 and notices the email addresses are in the phone column.
This converter scans every row before writing anything and builds the header from the union of all keys seen, in the order each one first appeared. Each row is then rendered against that same complete column list, and a key that row does not have becomes an explicit empty cell rather than a gap. Ragged input produces an aligned table.
The order rule has a consequence worth planning around: a field that only exists on your last record becomes the final column. If you want a sensible layout, make sure the first object in the array is a complete one.
Converting a batch of .json files
- Drop your
.jsonfiles into the box above, or click Choose files. Several at once is fine, and more can be added before you start. - Check the list. Each file is converted independently into its own CSV.
- Click Convert JSON to CSV. The progress bar advances one step per file.
- Download each result. Every CSV keeps its source filename with the extension swapped, so
orders.jsoncomes back asorders.csv.
RFC 4180 quoting, CRLF endings and Excel
Quoting is the part that quietly ruins spreadsheets, so it is handled to the letter of RFC 4180. A field is wrapped in double quotes whenever it contains a comma, a double quote, or a carriage return or line feed, and any double quote inside it is doubled. A field with none of those characters is written bare, which keeps the file readable.
Header cells go through the same escaping as data cells, which matters more than it sounds, because a key containing a comma is perfectly legal in JSON and would otherwise split your header row. Lines are joined with CRLF, as the specification requires.
This escaping is used across the site rather than reimplemented per tool: the CSV Viewer, the Fake Data Generator and many other modules import the same function, so a product description full of commas or a multi-line note behaves identically wherever it turns up.
One thing the file does not carry is a byte order mark. That is correct UTF-8, and it is also why Excel on Windows can mangle accented characters when you double-click the file instead of importing it.
Inputs that are not an array of objects
An array of objects is the expected shape, but a few others are accepted rather than refused. A single top-level object, not wrapped in an array, converts to a one-row CSV. An array of plain values such as ["a","b","c"] becomes a single column headed value, and so does any element that is not a plain object, including an array appearing directly at the top level.
An empty array is the one case that stops the run. There is no way to derive a column set from zero rows, and handing you a blank download would look like a broken tool rather than an empty input, so it fails instead. Invalid JSON fails too, and because files are processed in one loop, a single unparseable file aborts the whole batch.
If you are not sure your file is well formed, open it and paste its contents into the JSON Formatter first, since that page works from a textarea rather than a dropzone. Going the other way is CSV to JSON, and if you want a real spreadsheet rather than a text file, JSON to Excel writes .xlsx directly. More of these utilities sit on the developer tools hub.

