Turn JSON records into spreadsheet rows
Turns a JSON object, an array of objects or an array of arrays into a UTF-8 CSV or tab-separated file. It can flatten nested object paths, protects formula-like spreadsheet cells by default, and never uploads the source.
A JSON object, an array of objects, or an array of arrays up to 20 MB. It is parsed only in this tab.
How it works
What this works out
JSON and CSV do not disagree about punctuation; they disagree about shape. JSON can nest objects and arrays at any depth, omit a field from one record, and keep numbers, booleans and null as separate types. CSV is a rectangle of text cells. Converting between them therefore requires a declared answer to three questions: what counts as a row, which headings exist, and what happens to a value that is not already flat text.
This tool makes those decisions visible. It accepts only top-level structures that have an honest row model, discovers a stable header across the complete set, and either keeps nested values intact as JSON or flattens object paths. The downloaded bytes are then ordinary UTF-8 CSV or TSV, not a private format that needs this site to read it back.
The method
The row model comes entirely from the top level:
| JSON shape | CSV interpretation |
|---|---|
{ "name": "Ada" } | One object, one row |
[{ "name": "Ada" }, { "name": "Grace" }] | Each object is a row; keys are headings |
[["Ada", 36], ["Grace", 37]] | Each inner array is a row; headings are column_1, column_2, and so on |
[{}, []] | Refused: the rows do not share one model |
42, null or [] | Refused: there is no usable table |
For object rows, the header is not taken from the first record alone. Real API
data is sparse: the tenth record may be the first one with a middleName or
invoice.vatNumber. Every object is walked, and the first appearance of a key
adds it to the end of the header. That keeps the order predictable without
silently losing later fields.
The two nested modes answer a different question:
Keep as JSON in one cell preserves a nested value as compact JSON. A value
such as { "city": "Berlin", "postcode": "10115" } occupies one quoted
cell and can be parsed back without guessing which headings belonged to it.
Flatten objects to dot paths makes address.city and address.postcode
separate columns. Dots and backslashes already present in a key are escaped, so
the literal key address.city becomes address\.city and cannot collide with
an object called address containing a key called city. Arrays still remain
JSON because position zero is not a field name.
Once each row has values under stable headings, the CSV rule is small but strict:
plain → plain
contains,comma → "contains,comma"
say "hello" → "say ""hello"""
two lines → "two
lines"
A quote inside a field doubles. A field containing the separator, a quote or a line break is surrounded by quotes. Rows end with carriage-return plus newline, which is the convention described for CSV and the one spreadsheet software handles consistently.
Spreadsheet formula protection
CSV has no type information. When spreadsheet software sees a cell beginning
with =, +, - or @, it may interpret the text as a formula rather than as
data. That matters when a name, note or imported API field came from somebody
else: exporting untrusted text can turn opening the resulting spreadsheet into
an action.
Protection is therefore on by default. A JSON string such as "=2+2" becomes
'=2+2, which a spreadsheet treats as text. A JSON number such as -2 remains
-2, because it is genuinely numeric rather than a string whose meaning is
being inferred. Headings receive the same check; field names can be untrusted
data too.
Before you read on
A later JSON record introduces a field called email that the first record did not have. What should happen to the CSV?
Add one email column at the point that key is first discovered, and write an empty email cell for records that do not carry it. A CSV header must describe the complete table, not merely its first row. Taking only the first object's keys is a tempting shortcut because it is fast and produces valid-looking output, but sparse JSON makes it a data-loss bug: a field that appears later vanishes with no error. The converter therefore scans every row for the union of keys before it writes the header.
A worked example
The unit test converts these two records with flattening, comma separation, the UTF-8 marker and formula protection enabled:
[
{
"name": "Ada, Ltd.",
"active": true,
"note": "Line 1\nLine 2",
"profile": { "city": "London" }
},
{
"name": "=2+2",
"active": false,
"profile": { "city": "Paris" }
}
]
The result is:
name,active,note,profile.city
"Ada, Ltd.",true,"Line 1
Line 2",London
'=2+2,false,,Paris
Four details are doing real work:
profile.cityis a flattened path, not an invented label.- The comma and the line break force their fields to be quoted.
- The second record has no
note, so its note cell is empty rather than allowingParisto slide one column left. =2+2receives an apostrophe because it came from a JSON string; the boolean values remain the typed texttrueandfalse.
The physical file is 95 bytes: 92 bytes of visible CSV and line endings plus the three-byte UTF-8 marker. The test asserts the entire string byte for byte, all four output figures, the filename, and the MIME type. Separate tests cover changing delimiters, TSV naming, union columns, nested JSON cells, escaped path segments, missing values, adversarial width, progress and cancellation.
What it does not do
It does not infer a relational database from arbitrary JSON. Nested arrays are
not exploded into child tables, objects are not joined by guessed identifiers,
and a primitive top level is not wrapped in an invented value column. Those
can all be useful operations, but each needs a schema decision the file itself
does not supply.
It also cannot preserve the distinction between null and missing in CSV. Both
become an empty cell because an empty cell carries no type. Writing the word
null would preserve one value at the cost of making a real string "null"
indistinguishable, so that convention would need to be chosen by the importer,
not silently imposed here.
Finally, it does not evaluate formulas, dates or locale-formatted numbers. A JSON number is written with JSON’s decimal syntax, a string stays a string, and the spreadsheet decides how to display it after import. The converter’s job is to move data into stable rows without changing what the source said.
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
Is the JSON file uploaded before it becomes CSV?
No. A worker in this browser tab decodes the file, parses the JSON, builds the rows and encodes the download. The browser test for this page loads a real fixture and fails if any request leaves the site while the file is present.
What happens when later JSON objects have different keys?
Every key gets one column in the order it first appears. Earlier or later records that do not carry that key get an empty cell, so values never slide into the wrong heading merely because one object omitted a field.
Why is there an apostrophe before some spreadsheet cells?
Text beginning with =, +, - or @ can be interpreted as a formula when a CSV is opened in spreadsheet software. Formula protection prefixes those strings with an apostrophe so untrusted exported data remains text. Turn it off only when the formulas are deliberate and the source is trusted.
Why does flattening leave a JSON array in one cell?
An object's key is schema; an array's length and order are data. Expanding array positions would let one long list widen the whole file and would imply that item zero has the same meaning in every row. Keeping the array as JSON preserves it without inventing that claim.
Should I choose comma, semicolon or tab?
Comma is the interoperable CSV default. Semicolon is useful in locales where the comma is a decimal mark and spreadsheet software expects a semicolon list separator. Tab is best when the text contains many commas and produces an honestly named TSV file.
What is the Excel UTF-8 marker and do I need it?
It is the three-byte sequence EF BB BF at the beginning of the file. It helps older Excel versions recognise UTF-8 without an import dialog, so names and non-Latin scripts open correctly. Modern data tools usually do not need it, and the advanced toggle can omit it.