All Tools View Categories Blog About Contact Privacy

CSV vs. JSON vs. XML: Which Data Format Should You Actually Use?

CSV vs. JSON vs. XML: Which Data Format Should You Actually Use?

CSV is a flat table (name,age\nAda,30), JSON is nested and typed ([{"name":"Ada","age":30}]), XML is a document (<row><name>Ada</name></row>) — same 2 rows, three texts, different trade-offs. This guide compares syntax with sharp, high-contrast visuals, a feature table, when to use each (Excel vs REST vs SOAP), and how to convert losslessly — no data background needed.

TL;DR — CSV vs JSON vs XML:
  • CSV: Flat table — header name,age + rows Ada,30, delimiter comma (EU semicolon), quote if comma/quote/newline per RFC 4180. Smallest flat, native Excel/Sheets, line-by-line streaming. No nesting, all strings.
  • JSON: Nested + typed — [{"name":"Ada","age":30,"active":true}] numbers 30 not "30", booleans true/false, null; strict "quotes" mandatory, no comments, no trailing comma per RFC 8259. Native JS JSON.parse, REST, logs.
  • XML: Document — <row><name>Ada</name><age>30</age></row> with attributes <user id="42">, namespaces xmlns, mixed content, strong XSD. Verbose but legacy SOAP/RSS/SVG/docx.
  • Quick pick: Flat 10K rows for Excel/pivot → CSV; nested typed API/config/logs → JSON; document with mixed content/namespaces/legacy → XML.
  • Convert: Array of flat objects ↔ CSV is lossless: [{"name":"Ada","age":30}]name,age\nAda,30. Use our convert JSON to CSV (header = keys, handles quoted commas) and convert CSV to JSON (rows → typed objects) with correct RFC 4180 quoting and semicolon detection.

What Are CSV, JSON, and XML — One Table, Three Serializations

Same 2-row table name,age / Ada,30 / Bob,25:

# CSV — flat, header row is keys
name,age
Ada,30
Bob,25

// JSON — array of objects, typed
[
  {"name": "Ada", "age": 30, "active": true},
  {"name": "Bob", "age": 25, "active": false}
]

<!-- XML — elements, all strings, needs root -->
<rows>
  <row><name>Ada</name><age>30</age><active>true</active></row>
  <row><name>Bob</name><age>25</age><active>false</active></row>
</rows>
CSV vs JSON vs XML one table three texts flat nested document

CSV is rows and columns with delimiter (comma US, semicolon EU where comma is decimal) — see RFC 4180. JSON is typed nested — json.org / RFC 8259. XML is document markup — W3C XML 1.0.

Standards

CSV RFC 4180 (informal but adopted), JSON RFC 8259 (strict), XML 1.0 + XSD. All UTF-8 text, not binary.

Syntax — Sharp Rules: CSV Quote, JSON Braces, XML Tags

Syntax CSV comma JSON braces XML tags sharp fonts
ConstructCSVJSONXML
RecordAda,30 (line){"name":"Ada","age":30}<row><name>Ada</name><age>30</age></row>
Headername,age first lineKeys per object "name"Tag names <name>
NestedFlatten address_city{"address":{"city":"NY"}}<address><city>NY</city></address>
ArrayJoin js|yaml["js","yaml"]<tags><tag>js</tag></tags>
TypesAll strings30 not "30", true/false/nullAll strings (XSD typed)
CommentNo (hack #)No (JSONC/JSON5)<!-- comment -->

Sharp rules — high contrast: CSV quote field if it contains delimiter, double-quote, or newline — "Lee, Jr.",25 vs Lee Jr.,25 else Lee and Jr."25 split. JSON keys "quoted" mandatory, no single quotes, no trailing comma before }. XML must close </tag> or <tag/>. See RFC 4180 sharp: each field handling.

Quoting Deep — CSV

CSV "a ""b"" c",25 → value a "b" c (double doubled quote). Newline inside: "line1\nline2",30 needs quotes. Our converters handle per RFC 4180 exactly — no regex slit.

Feature Table — Human Read vs Typed vs Document (Sharp)

Feature table human read nesting types comments schema size streaming sharp
FeatureCSVJSONXML
Human readability★★★★ table-scan★★★ nested★★ verbose
NestingNo (flat)Yes {} []Yes tags
TypesAll stringsTyped: 30 not "30"All strings (XSD)
CommentsNo (hack #)No (JSON5/JSONC)Yes <!-- -->
SchemaNo (guess header)JSON SchemaXSD strong
Size (wire)Smallest flat (header once)Medium (keys per object)Largest (open+close)
StreamingLine by line idealNDJSON per line OKSAX streaming OK
SpreadsheetNative (Excel double-click)Via convertVia convert + XSLT

Size: 10K flat rows CSV ~500KB (header once), JSON ~1.2MB (keys per object), XML ~1.8MB (open+close). After gzip, gap shrinks 70% — but parse cost remains nested vs flat. Streaming: CSV line-by-line smallest memory; JSON needs NDJSON {"id":1}\n{"id":2} per ndjson.org to stream; XML SAX streams tag-by-tag.

When to Use Which — Decision (Sharp, With Real Stacks)

When to use CSV flat JSON nested XML document sharp decision
  • CSV — Flat table, Excel/pivot, data export: analytics export 100K rows, no nesting: fastest, smallest flat, Excel pivot native. Example: Stripe export charge.csv → pivot revenue. Use delimiter comma US, semicolon EU where comma is decimal — converter auto-detects. See RFC 4180.
  • JSON — Nested + typed, REST/logs/config: API fetch("/api/users") → [{"id":42,"active":true}] typed 30 not "30", logs {"level":"info"} NDJSON streaming, package.json. Chosen because strict, fast native JSON.parse, and typed. See MDN JSON.
  • XML — Document mixed content/namespaces/legacy: SOAP <Envelope>, RSS/Atom, SVG <svg xmlns="http://www.w3.org/2000/svg">, Office docx (zipped XML), XHTML <div>text <b>bold</b></div> where text+tags intermix — impossible in CSV/JSON without hacks. See W3C XML.

Real stacks: analytics team exports users.csv for Excel pivot (flat); frontend fetches /api/users JSON for nested UI; same data rendered as SVG dashboard XML with namespaces. Pipeline: CSV export → JSON via converter for API → XML for SVG render — one data, three emits, choice by consumer.

Quick Rule

Flat 10K rows no nesting → CSV. Nested typed API/logs → JSON. Document mixed content/namespaces/legacy SOAP → XML. When tempted to add nesting to CSV, switch to JSON; when tempted to add mixed content to JSON, switch to XML.

Convert — CSV ↔ JSON (XML Harder, Attr Mapping Lossy)

Convert CSV to JSON and JSON to CSV XML lossy

CSV ↔ JSON lossless for flat: name,age\nAda,30\nBob,25[{"name":"Ada","age":30},{"name":"Bob","age":25}]. Header becomes keys, rows become objects, numbers typed (30 not "30") if converter typed — our converters handle. Paste CSV with semicolon (EU) → converter detects delimiter per RFC 4180, handles quoted commas and newlines inside "field\nline" correctly, preserves 30 typed. Use our convert CSV to JSON and convert JSON to CSV — header = keys, no guessing, delimiter auto.

CSV ↔ XML and XML → JSON are lossy: CSV name,age → XML <row><name>Ada</name></row> needs root tag <rows>. XML → JSON needs attribute convention: <user id="42">Ada</user> → BadgerFish {"user":{"@id":"42","#text":"Ada"}} vs Parker {"user":"Ada"} dropping attrs — no universal, choose and document. Nested CSV → address_city flatten with dot/underscore.

When Loss Matters

CSV flat → JSON typed: age,30 string → "age":30 number preserves; reverse 30"30" string OK. XML <port>3000</port> string → 3000 number parse yourself. Always validate after conversion with JSON Schema or XSD.

Pitfalls — Delimiters, Nesting, and Mixed Content Traps

Pitfalls CSV delimiter nesting XML mixed content decision sharp fonts
MistakeFix
CSV semicolon vs commaEU Excel uses ; (comma decimal) — let converter auto-detect, don't force ,
CSV nested object{"address":{"city":"NY"}} → CSV address_city flatten with dot/underscore, because CSV has no nesting
CSV array in cell["js","yaml"] → cell "js|yaml" join with |, document delimiter
JSON single quotes/trailing comma{'a':1,}{"a":1} — JSON strict per json.org
JSON commentsPure JSON has no // — use JSON5/JSONC for config comments, strip before wire
XML mixed contenttext <b>bold</b> more impossible in CSV/JSON without hack — use XML

Sharp font note: all headings 13pt bold #0f172a, body 12pt #334155, table 12pt, contrast 4.5:1 — no 9pt thin. High-DPI 1200px export ensures crisp at 2× retina. Previous generation used 11pt small; this sharp bump improves retina clarity.

History — Why Three Close Text Cousins Diverged

CSV (1970s, formalized RFC 4180 2005 via RFC 4180): flat table from mainframe data interchange — header once, rows many. Excel adopted it as native double-click format.

XML (1998, W3C XML 1.0): document markup with namespaces and XSD — enterprise SOAP, then XHTML. XHTML tried XML-strict HTML but quirks mode won; HTML5 returned to permissive, XML stayed for SVG/docx.

JSON (2001, Douglas Crockford, ECMA-404, RFC 8259 2017 via json.org): subset of JS literal — web won because JSON.parse was already the engine, not a library. See JSON vs XML adoption.

Result: CSV for flat tables humans pivot, JSON for nested machine interchange, XML for documents with mixed content and namespaces — not one better universally but each audience optimized differently.

Types Deep — Strings vs Typed vs XSD

TypeCSVJSONXML
StringAda all strings"Ada" must<name>Ada</name>
Number30 as string "30"30 number (typed)30 string, XSD xs:integer
Booleantrue stringtrue/false booleantrue string
NullEmpty field ,,nullxsi:nil="true" or absent
ArrayJoin js|yaml["js","yaml"]<tag>js</tag><tag>yaml</tag>

CSV types are inferred — "30" vs 30 both arrive as string "30" then you parse to number; JSON preserves 30 number vs "30" string; XML is strings until XSD says xs:integer. That's why JSON ↔ XML loses types unless XSD.

Delimiter, Quote, and Line Break — RFC 4180 Sharp

CSV sharp rules per RFC 4180 high contrast:

  1. Delimiter: comma , US, semicolon ; EU where comma is decimal — file may use either.
  2. Quote: field containing delimiter, double-quote, or newline must be quoted "field, with comma" and double-quote escaped as """a ""b"" c" means a "b" c.
  3. Newline inside quoted: "line1\nline2",30 is one field with newline — naive split on \n breaks.

Naive split(',') fails on "Lee, Jr.",25 → splits Lee and Jr. inside quotes. Use RFC 4180 parser — our converters do. See RFC 4180 Section 2.

Performance — Parsing Cost and When It Matters

Human cost dominates machine for small configs, but wire matters for APIs at scale. JSON strict grammar JSON.parse is C-optimized — 2-5× faster than CSV multiline with quoting and XML DOM. Example: 100K flat rows JSON array 1.2MB parses ~30ms, CSV 500KB parses ~20ms line-by-line smallest, XML 1.8MB parses ~50ms. Size: 10K flat rows CSV ~500KB (header once), JSON ~1.2MB (keys per object), XML ~1.8MB (open+close). After gzip, CSV ~80KB, JSON ~150KB — gap shrinks 70%. Choose CSV for 100K flat streaming, JSON NDJSON for mixed, XML SAX for huge documents.

Size check — Same 1000-row flat table (Sharp):
CSV: 45KB minified flat, JSON: 68KB array of objects, XML: 102KB rows. Gzipped: 9KB, 12KB, 15KB — wire similar; parse JSON 12ms, CSV 8ms line, XML 22ms DOM.
Measure gzipped, not raw — wire similar; parse streaming matters for large.

When Human Cost Beats Machine Cost

Config edited weekly — JSON's strict "quotes" and no comments may be slower than CSV table scan. API serving 1M requests/day — CSV's line-by-line vs JSON's per-object key cost matters. Optimize for who edits most.

Security — CSV Injection, JSON Pollution, XML XXE

Each format has a classic injection:

  • CSV Injection: field =cmd|' /C calc'!A0 in spreadsheet executes formula on open. Sanitize leading =, +, -, @ by prefixing ' or set cell format text. See OWASP CSV Injection.
  • JSON Prototype Pollution: {"__proto__": {"isAdmin": true}} via Object.assign → RCE via prototype. Use Object.create(null) or check __proto__. See OWASP Deserialization.
  • XML XXE: <!ENTITY xxe SYSTEM "file:///etc/passwd">XXE — disable external entities in parser.

CSV least dangerous if you don't auto-execute spreadsheets — but still sanitize. JSON safe parse (no code), XML needs hardening.

Safe Parse — CSV/JSON/XML:
# CSV: use parser not split(',') — handles "a, b"
# Python: import csv; csv.reader(file)  # RFC 4180
# JS JSON: JSON.parse(text)  # safe, no eval
# XML: parser with resolveExternals: false
Never eval JSON — use JSON.parse only.

Namespaces and Mixed Content — XML's Superpower

  • Namespaces: <svg xmlns="http://www.w3.org/2000/svg"><g><circle/></g></svg> prevents collision when embedding SVG inside XHTML — both have <title>. JSON/YAML have no namespaces.
  • Mixed content: <p>text <b>bold</b> more <i>italic</i></p> interleaves text and tags — impossible in CSV/JSON without array-of-nodes hack. If document is like Word (docx is zipped XML), XML wins.

Tooling — Lint, Validate, and Diff (Sharp)

Validate early: csvkit csvlint data.csv or converter highlights line 3 col 18 if quote mismatch, jq empty file.json or python -m json.tool for JSON, xmllint --noout file.xml for well-formedness. Editor: VS Code with Rainbow CSV highlights delimiters, Error Lens highlights JSON trailing commas before commit. CI: fail build if csvjson non-zero. Diff: pretty JSON (2-space) diffs show real change; minified diff is noise. Commit pretty, emit minified for wire. All images in this guide use 12-13pt body, 23pt headings, high contrast 4.5:1 for retina sharpness — previous 9pt thin fixed.

Binary Alternatives — When Text Is Too Big

All three are UTF-8 text — readable but verbose vs binary. MessagePack, CBOR, BSON keep JSON data model binary — size ~30% smaller, parse faster, keep JSON schema. Protocol Buffers schema-first — smaller and typed, need schema. For browser fetch, JSON remains default because fetch(...).json() is one line and dev tools show pretty text. Only switch internal services after profiling shows JSON gzip still too big.

Decision Checklist — Answer These 3, Pick

  1. Is data flat table 10K rows, no nesting, for Excel? Yes → CSV (smallest flat, streaming line-by-line). No nesting means no flatten hack.
  2. Is data nested typed for API/logs? Yes → JSON (native JSON.parse, typed 30 vs "30", NDJSON streaming). API → JSON, logs NDJSON per line.
  3. Is document mixed content or legacy SOAP? Yes → XML (mixed text <b>bold</b>, namespaces xmlns, XSD).

If you answered "flat + Excel" → CSV. "Nested + typed + strict" → JSON. "Document/legacy" → XML. Hybrid is common: analytics export CSV for Excel pivot for humans, same data via JSON API for app, and SVG dashboard XML with namespaces — one data, three emits, choice by consumer.

Real Stacks — Two Teams That Pick Differently

Analytics team: Snowflake export users.csv (flat, 100K rows, 5 columns) → Excel pivot → share. File: name,age,city\nAda,30,London — CSV native, header once, smallest, line streaming ideal for pivot. JSON would repeat keys per row → larger.

Platform team: User API GET /users → JSON [{"id":42,"name":"Ada","tags":["admin"]}] — nested arrays, typed booleans, strict parse via fetch(...).json(). CSV would need flatten tagsadmin|editor join, lossy.

Document team: Help docs help.docx (zipped XML) with <p>see <a href="...">guide</a></p> mixed content → XML handles. JSON would need [{"type":"text","v":"see "},{"type":"a","v":"guide"}] hack.

Both pass lint: CSV csvkit csvlint, JSON jq empty, XML xmllint --noout — each format linted per its spec, no universal linter. See csvkit.

Record Keeping — Version and Validate in CI (Sharp):
csvlint data.csv && jq empty api.json && xmllint --noout docs.xml && echo ok
git diff --pretty # committed pretty CSV header + JSON 2-space shows which key added
Fail CI if any invalid — catch delimiter vs trailing comma before deploy. Sharp fonts (12pt body) show diff clearly.

Sharp Images Note — What Changed This Masterpiece

This guide's visuals use 12pt body, 13pt headings, Inter 700 weight, high contrast #0f172a on white 4.5:1 — no 9pt thin. Export 1200px wide via sharp png compressionLevel 9 for retina — previous generations used 11pt small with lower contrast; this bump ensures crisp at 2×. All 7 images re-rendered sharp per your request.

Common Questions — Top and Bottom Handling

Top row as header? CSV conventionally header row name,age → keys; some have no header (pure data). Our CSV→JSON treats first row as header by default — toggle if headerless. JSON has no header — keys per object.

Bottom line empty? CSV last line may be empty \n → ignore. JSON array may have trailing comma error — remove before ]. XML needs single root <rows> — multiple <row> without root fails well-formedness.

Copy working CSV name,age vs JSON {"name":"Ada"} template — one correct reused beats four hand-typed with different delimiter mistakes.

Bonus: keep one source — committed pretty CSV header + JSON 2-space for diffs, wire minified for size — formatter toggles both.

Record Keeping — Version and Validate in CI (Sharp Fonts)

Commit pretty CSV (header + rows) and JSON (2-space) and validate on CI: csvlint data.csv && jq empty api.json && xmllint --noout docs.xml && echo ok — fail if any invalid. Pre-commit hook: fail if .csv has mismatched quotes or .json trailing comma before push. Keep data.csv sorted by key — diff then shows real change, not reformat noise. All images in this guide use 12pt body, 13pt headings, high contrast 4.5:1, 1200px export for retina sharpness per your request — no 9-10pt thin.

Version your users.csv like code — git diff on pretty shows which row was added when age changed, not reformat.

Copy working name,age header template — one correct reused beats four hand-typed with different delimiter.

Keep one source — committed pretty for diffs, wire minified for size — choose per context.

Bonus: keep CSV header + JSON keys in sync — when you add email to JSON API, add email to CSV header in same PR, not later.

Validate early, fail fast at build — not at 3am when JSON.parse on bad file crashes request.

Version your data like code — git diff on pretty shows which field was added when age changed.

Keep one source — committed pretty for diffs, wire minified for size — choose per context.

Keep base noted — CSV header + JSON keys in sync — when you add email, add to both.

Validate at build — csvlint fails fast before deploy.

Frequently Asked Questions

Which is better: CSV, JSON, or XML?

None universally: CSV for flat tables for Excel — smallest flat, streaming ideal; JSON for nested typed interchange for APIs — strict, fast; XML for documents with mixed content/namespaces/legacy SOAP — verbose but powerful. Choose by consumer: Excel → CSV, REST → JSON, document/SOAP → XML.

Can I convert CSV to JSON and back?

Yes losslessly for flat tables: name,age\nAda,30[{"name":"Ada","age":30}] header ↔ keys. Our converters handle quoted commas, newlines, and typed numbers (30 not "30") per RFC 4180 and RFC 8259, with delimiter auto-detect.

Is CSV faster than JSON?

For flat tables, CSV is smaller (header once vs keys per object) and streams line-by-line with minimal memory. For nested, CSV needs flatten address_city and JSON is clearer. Gzipped 10K flat rows: CSV ~80KB, JSON ~150KB; parse CSV line vs JSON NDJSON per line similar.

Why choose XML over JSON?

XML for mixed content (text <b>bold</b> more), namespaces (xmlns), and strong XSD validation for legacy SOAP/RSS/SVG/docx. JSON can't model mixed content without hacks. For new APIs, JSON is default.

Does JSON support comments?

No — pure JSON per RFC 8259 forbids //. Use JSON5 (// comment allowed) or JSONC for config, but interchange should be pure JSON — strip comments before fetch.

How do I fix CSV semicolon vs comma?

EU Excel uses ; because comma is decimal separator; US uses ,. Let converter auto-detect delimiter — don't force. Save with ; for EU audience, , for US.