JSON vs CSV
Hierarchical versus tabular. The choice is decided almost entirely by whether your data has nesting — and by who opens the file at the other end.
Two Shapes of Data
CSV describes a grid: rows and columns, every row the same width. JSON describes a tree: values inside values, to any depth. Nearly every difference between them follows from that.
# CSV
id,name,email,city
101,Alice,alice@example.com,Chennai
102,Bob,bob@example.com,Bengaluru// JSON
[
{ "id": 101, "name": "Alice", "email": "alice@example.com", "city": "Chennai" },
{ "id": 102, "name": "Bob", "email": "bob@example.com", "city": "Bengaluru" }
]For data that genuinely is a table, CSV is the better fit — it is smaller, it opens in Excel, and the header row states the schema once. The moment the data stops being a table, CSV stops working and JSON keeps going.
Head to Head
| JSON | CSV | |
|---|---|---|
| Structure | Hierarchical, any depth | Flat grid only |
| Types | Explicit — string, number, boolean, null | None — everything is text |
| Size (raw) | Larger — keys repeat per record | Smaller — header written once |
| Size (gzipped) | Close to CSV | Marginally smaller |
| Standardised | Yes — RFC 8259, strictly | RFC 4180, widely ignored |
| Streaming | Awkward — needs JSON Lines | Natural — one record per line |
| Opens in a spreadsheet | No | Yes |
File Size, Honestly
CSV's size advantage is real and frequently overstated. JSON repeats every key on every record, so on a wide table with many rows the raw difference is large. But payloads are almost always compressed in transit, and repeated keys are exactly what compression algorithms eliminate best.
The same effect is measured in detail on the minifier page: stripping repetitive characters from a JSON payload cut 41% of raw bytes but only about 11% after gzip. Repeated keys behave the same way. If your concern is bandwidth rather than disk, enabling compression matters far more than choosing CSV.
Where CSV's compactness genuinely wins is bulk analytical storage — millions of rows at rest, where even compressed size and scan cost matter. At that scale, though, a columnar format like Parquet usually beats both.
CSV Has No Types
Every CSV cell is text. What it means is inferred by whatever reads it, and different readers infer differently.
id,code,active,notes
101,00123,true,
102,1e5,false,N/AReasonable questions with no answer in the file itself:
- Is
00123the string "00123" or the number 123? Excel will strip the leading zeros and destroy the value. - Is
1e5a product code or the number 100000? - Is
truea boolean or the four-character word? - Is the empty
notescell an empty string, or null, or absent? - Is
N/Aa value, or a marker meaning "no value"?
JSON answers all of these in the document: "00123" is unambiguously a string, 100000 unambiguously a number, and null unambiguously distinct from "". This is the same class of problem as YAML's type inference, discussed on the JSON vs YAML page — except that in CSV there is no quoting convention that resolves it, because quotes are already used for something else.
The Standardisation Problem
RFC 4180 defines CSV, and much of the world ignores it. In practice you cannot assume:
- The delimiter is a comma. Locales that use a comma as the decimal separator often use semicolons instead, so Excel's output varies by the machine that produced it.
- Line endings are consistent. CRLF, LF, and occasionally CR all appear.
- The encoding is UTF-8. Latin-1 and Windows-1252 files are common, and a UTF-8 BOM appears often enough to break naive parsers.
- Quoting is uniform. Some writers quote every field, some only those containing the delimiter, some neither.
- There is a header row. Nothing in the format says so.
And the hardest case — a field containing the delimiter, a quote, or a newline:
id,name,quote
101,"Smith, Alice","She said ""hello"""
102,"Bob","A note
spanning two lines"That is valid RFC 4180: fields containing commas are quoted, embedded quotes are doubled, and a quoted field may contain literal newlines — which means you cannot parse CSV by splitting on commas and newlines, however tempting it looks. Use a real CSV library. JSON has exactly one escaping convention, defined precisely, implemented identically everywhere.
Converting Between Them
JSON to CSV works cleanly only when the JSON is a flat array of objects with consistent keys. Nesting has to go somewhere:
// Nested source
{ "id": 101, "address": { "city": "Chennai", "country": "IN" } }# Option 1 — flatten paths into column names
id,address.city,address.country
101,Chennai,IN
# Option 2 — embed JSON in a cell (defers the problem)
id,address
101,"{""city"":""Chennai"",""country"":""IN""}"Arrays of varying length are worse: either one column per possible index, or a delimited string inside a cell. Neither round-trips reliably.
CSV to JSON is mechanically easier — every row becomes an object keyed by the header — but you must decide types, since CSV has none. Does "123" become a number? Does an empty cell become "", null, or an omitted key? Answer these deliberately rather than letting a library guess.
# jq: convert a JSON array of flat objects to CSV
jq -r '(.[0] | keys_unsorted), (.[] | [.[]]) | @csv' data.jsonChoosing
Use JSON when
- Data is nested or records vary in shape
- Types must survive the trip
- It is an API request or response
- Null and empty must be distinguishable
- A schema will validate it
Use CSV when
- The data is genuinely tabular
- A human will open it in a spreadsheet
- It feeds a statistical or BI tool
- You are bulk-loading a database
- Streaming row-by-row matters
A common and sensible arrangement is to use JSON throughout the system and offer CSV as an explicit export for users who will open the file in Excel. Make it a separate endpoint or a ?format=csv parameter — never the default response of a JSON API.
Frequently Asked Questions
Which is smaller, JSON or CSV?
CSV, because it writes each column name once in a header row while JSON repeats every key on every record. On tabular data the raw difference is substantial — but after gzip compression it narrows sharply, since repeated keys compress extremely well.
Can CSV represent nested data?
Not natively. CSV is a flat grid, so nesting must be faked — by flattening paths into column names like address.city, or by embedding a JSON string inside a cell. Both work and both are awkward.
Is there a CSV standard?
RFC 4180 exists but is widely ignored, which is the format's central weakness. Delimiters, quoting, escaping, line endings and encoding all vary between producers, so a CSV file that one tool writes is not guaranteed to load in another.
How do I convert JSON to CSV?
Only cleanly if the JSON is a flat array of objects with consistent keys. Take the union of keys as the header row, then write one row per object. Nested values must be flattened or serialised into a cell first — there is no lossless general conversion.
Which should an API return?
JSON, in almost all cases. CSV is worth offering as an explicit export option for users who will open the result in a spreadsheet, but it should be a separate endpoint or a format parameter, not the default response type.