CSV, JSON, and YAML: When to Use Each

Updated July 27, 2026

CSV, JSON, and YAML all show up constantly in day-to-day development work, and it’s common to reach for whichever one you’re most used to rather than the one that actually fits the shape of the data. Here’s the real distinction.

The same data, three ways

Nothing clarifies the difference faster than seeing one record in each format.

CSV:

id,name,role
1,Alex Lee,admin
2,Sam Patel,editor

JSON:

[
  { "id": 1, "name": "Alex Lee", "role": "admin" },
  { "id": 2, "name": "Sam Patel", "role": "editor" }
]

YAML:

- id: 1
  name: Alex Lee
  role: admin
- id: 2
  name: Sam Patel
  role: editor

CSV is the most compact and the only one a spreadsheet opens natively. JSON is the most explicit. YAML is the easiest to edit by hand. For flat data like this, all three work — the differences only start to matter once the data stops being flat.

CSV: flat tables, nothing more

CSV, formalised by RFC 4180, represents exactly one shape well: a flat table of rows and columns, all the same structure. It can’t express nesting — a customer record with an address that itself has multiple fields either gets flattened into extra columns (address_street, address_city, …) or awkwardly serialised as a string inside one cell.

It also has no types. Every value is text, so 007 and 7 are indistinguishable to the reader, and every consumer has to guess. If your data is genuinely tabular — a spreadsheet export, a list of transactions — CSV is the simplest, most portable choice, and its git diffs are readable in a way the other two struggle to match at scale.

JSON: nested data, built for machines

JSON represents arbitrarily nested structures — objects containing arrays containing more objects — which makes it the natural fit for API responses, structured config, or any data with a hierarchy CSV can’t express. It’s strict and unambiguous: every value has an explicit type, with no room for the “is this a date or a string?” question CSV forces.

The cost is verbosity — every key repeats in every object, and the punctuation adds up — and it’s not pleasant to hand-edit, especially deeply nested. It also has no comments, which is precisely why it makes an awkward config format: you cannot explain why a setting is what it is.

Two gotchas worth knowing. JSON has no date type, so dates travel as strings by convention (ISO 8601 is the sane choice). And JSON numbers are IEEE 754 doubles, so integers beyond 2^53 lose precision silently — a real problem for 64-bit IDs, which is why APIs that use them send them as strings.

YAML: JSON’s structure, written for humans

YAML represents the same nested data JSON does, but uses indentation and minimal punctuation instead of braces and quotes, which makes it dramatically easier to read and hand-edit. It supports comments. That’s exactly why YAML dominates configuration — CI pipelines, Kubernetes manifests, app config — where a person writes and reviews it directly.

YAML is also a superset of JSON, so any valid JSON is valid YAML. That’s occasionally useful: you can paste JSON into a YAML file and it parses.

The tradeoff is that YAML’s flexibility invites subtle bugs, and they’re worth knowing before you get bitten:

The Norway problem. In YAML 1.1, unquoted no, yes, on, off, y, and n parse as booleans. A country-code list containing NO for Norway silently becomes false. YAML 1.2 fixed this, but many parsers still default to 1.1 behaviour. Quote your strings.

Whitespace is load-bearing, and tabs are illegal. A tab character where spaces are expected is a hard parse error, and the message is rarely helpful about where.

Version numbers become floats. version: 1.10 parses as the number 1.1. Quote it.

Sexagesimal surprises. In YAML 1.1, an unquoted 22:30 parses as the number 1350 — base-60 notation, a feature nobody asked for.

The pattern is consistent: quote anything that isn’t unambiguously a plain string, and prefer parsers in YAML 1.2 mode.

What about TOML?

TOML is worth a mention as the fourth option, increasingly common in Rust and Python tooling (Cargo.toml, pyproject.toml). It aims at YAML’s use case — human-authored config with comments — without the significant-whitespace and implicit-typing hazards.

It’s excellent for flat-to-moderately-nested config and gets awkward with deep nesting, where YAML stays more readable. If you’re choosing a config format fresh and your structure is shallow, it’s a genuinely good default.

The short version

Your data Use
Flat, tabular, spreadsheet-bound CSV
API payloads, machine-to-machine JSON
Config a person edits, deeply nested YAML
Config a person edits, shallow TOML
Needs comments Not JSON
Needs 64-bit integer precision Not JSON numbers

Converting between them

CSV to JSON and JSON to CSV handle the tabular↔nested boundary. CSV to JSON infers types and structure from your columns; JSON to CSV flattens objects into a key-union header row — worth understanding, because flattening genuinely nested data into a table is lossy by nature, and deeply nested input will produce a wide, sparse result.

JSON to YAML and YAML to JSON convert between the two nested formats directly, useful when a config file needs to move from one tool’s expected format to another’s. Round-tripping YAML → JSON → YAML is a quick way to normalise a messy hand-edited file, though it will discard your comments — JSON has nowhere to put them.

Share