In short: RowSlice's JSON to CSV converter reads a JSON array of objects, or NDJSON, and writes one CSV row per object — in your browser, with nothing uploaded. Nested objects become dotted column names such as customer.address.city. Arrays stay in a single cell as JSON text rather than becoming columns. The header is the union of the keys across every row, so records that do not all carry the same fields still line up. Anything in the file that is not an object is counted and reported, and the run carries on. The document is never handed to a parser whole, so a file far larger than memory converts without the tab falling over.
How to Convert JSON to CSV
- Choose or drag in the
.jsonor.ndjsonfile. - Leave What the file holds on Detect automatically unless the file is unusual. The first character that is not whitespace decides it, and the next section shows what each one looks like.
- Leave Flatten nested objects ticked to get dotted column names. Untick it to keep each nested object whole, in one cell, as JSON.
- Press Convert to CSV. It reads the file twice — once to work out the columns, once to write the rows — and the progress bar covers both passes.
- Download
yourfile.csv. The result gives the number of columns it settled on and the number of elements it had to skip, which is the first thing to check when the row count is not what you expected.
The Two Shapes It Accepts
A top-level array of objects:
[
{"id": 1, "status": "open"},
{"id": 2, "status": "closed"}
]or NDJSON, the same records with one object per line and no punctuation holding them together:
{"id": 1, "status": "open"}
{"id": 2, "status": "closed"}Both give the same two-row CSV. The detection is one rule: the first character in the file that is not a space or a line break. An opening square bracket means the array; anything else means one object per line. There is a setting to say which it is, for the rare file that guesses wrong.
For the array shape, indentation is irrelevant — the file is cut into elements by counting brackets and braces, not by reading lines, and a brace inside a quoted value does not count. A pretty-printed array whose objects each span ten lines reads exactly like a compact one. For NDJSON the line is the record, so a blank line is skipped as a separator rather than counted as a broken row, and a Windows line ending is handled.
What Each JSON Value Becomes in the CSV
Every value in the document lands in the CSV in one of these ways. The sections after the table work through the two that surprise people most, nested objects and arrays.
| In the JSON | In the CSV | Why |
|---|---|---|
A string or an ordinary number, "city": "Leeds" | The text the file holds, in a column called city | Values are copied rather than interpreted: no date is reformatted and no number is rounded on purpose |
A nested object, "customer" holding "address" holding "city" | One column per leaf, named for the path that reaches it: customer.address.city | Flattening goes 20 levels deep; a branch deeper than that goes into one cell as JSON text |
An array, "tags": ["vip","trade"] | One cell holding ["vip","trade"] as JSON text | Spreading an array across columns would let whichever record has the most entries set the width of the table |
"note": null | An empty cell | A CSV has no way to write null that a reader would not take as the four-letter word |
| A key that only some records carry | A column of its own, empty in the records that do not carry it | The header is the union of the keys found across the whole file, up to 1,024 columns |
true and false | true and false | Written as the words JSON uses for them, in lower case |
| A number longer than 15 significant digits, such as a 19-digit id | Every digit in Chrome and Edge; the rounded double in Firefox | A JSON number is an IEEE 754 double, and Chrome 114 and later also hand the reader the number's original text |
| An element that is not a JSON object: a bare string, a number, a broken line | No row at all; it is counted as skipped | One bad line out of a million must not end the run, so the count and the first five are reported instead |
How Nested Objects Become Columns
A CSV has one level. JSON usually has several, so every leaf gets a column named for the path that reaches it, joined with dots. Two records from an order export, with the second missing a postcode:
{"id":1,"customer":{"name":"Ann Poole","address":{"city":"Leeds","postcode":"LS1 4AP"}},"note":null}
{"id":2,"customer":{"name":"Bo Chen","address":{"city":"Derby"}},"note":"call back"}come out as five columns:
id,customer.name,customer.address.city,customer.address.postcode,note
1,Ann Poole,Leeds,LS1 4AP,
2,Bo Chen,Derby,,call backThree things happened there worth naming. The header is the union of the keys found across the whole file, not the keys of the first record, which is why customer.address.postcode is a column even though only one row fills it. Columns appear in the order they are first seen. And "note": null became an empty cell, the same cell a key that is absent altogether gets — a CSV has no way to write null that a reader would not take as the word.
Two limits apply, and both are reported rather than passed over. Flattening stops at 20 levels; past that the rest of the branch goes into a single cell as JSON, because a twenty-segment path is not a heading anyone reads. And the header stops at 1,024 columns: the list of keys is the one thing here that grows with the data, and a spreadsheet a thousand columns wide is past being useful. Keys past the cap are left out and the result says so. Untick Flatten nested objects and none of this happens — each top-level key gets one column, and anything nested under it sits in the cell as JSON.
What Happens to Arrays
An array is kept in one cell, written as JSON text. It is the choice that surprises people most, so here is the reasoning, with three records where the array has two entries, none, and one:
{"sku":"A-77","tags":["vip","trade"]}
{"sku":"B-12","tags":[]}
{"sku":"C-03","tags":["clearance"]}sku,tags
A-77,"[""vip"",""trade""]"
B-12,[]
C-03,"[""clearance""]"The alternative is to spread an array across columns — tags.0, tags.1, and so on. It looks tidy on the first three rows and falls apart on a real file. The width of the table would be set by whichever single record happens to carry the most entries, so one product with forty tags gives every other row thirty-nine empty cells; the header could not be written until the last record had been read; and the column a value lands in would depend on its position in an array, which in most exports means nothing at all. Keeping the array intact is lossless, the width is stable, and any consumer can parse the cell back.
The doubled quotation marks in the output are not damage — they are how CSV quotes a value that contains a comma and a quote. A CSV reader gives you ["vip","trade"] back. An empty array needs no quoting at all, which is why B-12's cell is a bare []. The number of cells written this way is counted with the result, which is a useful signal: a high count means the export is not really a table yet.
Rows It Has to Skip
Real exports have a broken line in them — a truncated download, a log file with a stray message in it, a hand-edited record. One bad line out of a million must not end the run, so anything that is not a JSON object is counted and the conversion continues. This NDJSON has an unclosed brace on line 2 and a bare string on line 4:
{"id":1,"status":"open"}
{"id":2,"status":"closed"
{"id":3,"status":"open"}
"just a string"
{"id":4,"status":"open"}and converts to three rows:
id,status
1,open
3,open
4,openThe result reports 2 skipped and shows both, each with the line number it was on and the start of its text, cut to 200 characters and put on one line so that a huge element cannot flood the report. At most the first five are shown, however many there are. Three reasons are distinguished: not valid JSON, valid but not an object, and too deeply nested to read — enough to tell a truncated file apart from a file of the wrong shape.
The one case that stops the run is a file where nothing is an object, because there are no rows to write and an empty CSV would be a worse answer than a message. Then it says how many elements it read and shows the first, which is usually all you need to see what the file really is. If you would rather check the CSV afterwards than trust the counts, the validator reads the written file and reports its rows, quoting and encoding.
How Big a JSON File Can It Convert?
This is where most JSON-to-CSV converters give up, and it is worth being plain about why. The obvious implementation reads the file into a string and calls a JSON parser on it. That parser builds the whole document as objects in memory before it returns anything, and an object graph costs more than the text it was built from, so a 2 GB export takes the tab with it long before the first row is written.
Nothing here is parsed whole. The file is read about 4 MB at a time and cut into top-level elements by counting brackets and braces as the bytes arrive — tracking whether the scanner is inside a quoted string, so the brace in {"note":"{not a brace}"} does not confuse it. Each element is parsed on its own and thrown away once its cells are written. What stays in memory is one element and one slot per column, never one per row.
The file is read twice, and the reason is worth knowing because it shows up in the timing: a CSV header has to be written before the first row, and which columns exist is only known once every record has been read. The first pass collects the keys, the second writes the rows. Holding the rows between the two would be faster and is the one thing this engine will not do. The progress bar covers both passes, so a run that reaches halfway has read the file once.
The result goes to your browser's private on-disk storage (OPFS) as it is produced, inside the allowance your browser gives one site — about 10 GB in Firefox. The CSV is usually much smaller than the JSON, because the keys are written once in the header instead of on every record. Afterwards, the analyzer will tell you what you have ended up with — row count, column profiles, the range of the values — without opening the file in anything, and the viewer pages through the rows themselves when the CSV is still too big for a spreadsheet. Two pulls from the same endpoint, converted the same way, line up as well: matching the two CSVs on an id column reports which records were added, removed and changed between the two pulls.
What It Does Not Do
It does not pick a branch out of a document. There is no path expression and no convert the array under this key option: the tool reads a top-level array of objects, or one object per line, and nothing else. A response wrapped in {"data": [ … ], "meta": { … }} has to be unwrapped first.
It does not guess types. Values go into the CSV as the text the file holds, and a CSV carries no types anyway. Nothing is reformatted, no date is reinterpreted, no number is rounded on purpose — the one thing the browser can impose is the double at the heart of every JSON number, which is why the FAQ above is specific about long identifiers. What you get is a faithful flattening, not a cleaned-up one.
It reads JSON, not JavaScript. Comments, trailing commas, single-quoted keys and unquoted keys are not JSON, and a record containing them is counted as skipped rather than repaired.
It writes CSV, not a workbook. One file in, one file out. For an .xlsx that keeps leading zeros and long identifiers intact, convert this CSV again with the CSV to Excel converter, or follow the guide to importing it by hand if you want to choose each column's type yourself. To go back the other way, the CSV to JSON converter writes an array or NDJSON from a CSV.
Last updated .