JSON vs YAML
On this page
TL;DR. JSON for APIs and data interchange. YAML for human-edited configuration. Both can represent the same data, but their design priorities are opposite — JSON optimizes for parser simplicity, YAML for human writability.
At a glance
| JSON | YAML | |
|---|---|---|
| Spec | RFC 8259 (2017) | YAML 1.2.2 (2021) |
| Readability | OK | better |
| Writability (by humans) | clunky | excellent |
| Parser simplicity | trivial | complex |
| Comments | no | yes (#) |
| Trailing commas | no | n/a |
| Multi-document support | no | yes (---) |
| Anchors / aliases (refs) | no | yes (& / *) |
| Tab vs space sensitivity | none | tabs forbidden |
| Common use | APIs, logs, web data | k8s, GitHub Actions, Hugo front matter |
| Performance | fast | slow (relative) |
Same data, both formats
JSON:
{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {
"name": "demo",
"labels": {
"env": "dev",
"team": "platform"
}
},
"data": {
"LOG_LEVEL": "info",
"FEATURE_X": "true"
}
}
YAML:
apiVersion: v1
kind: ConfigMap
metadata:
name: demo
labels:
env: dev
team: platform
data:
LOG_LEVEL: info
FEATURE_X: "true"
Same content. The YAML version is shorter, has no quotes around most strings, and is clearly hand-editable. The JSON version is unambiguous and parses faster.
Where JSON wins
- Universally supported. Every language ships a JSON parser. YAML usually requires a third-party library.
- Fast and predictable. Parser is a few hundred lines. No surprises.
- Wire-efficient with gzip. JSON’s repetitive structure compresses well.
- Logs, observability, structured events. Single-line JSON is grep- and jq-friendly. YAML’s multi-line nature breaks log pipelines.
- APIs. REST, GraphQL responses, webhooks — all JSON. The “use YAML in HTTP body” path is unsupported nearly everywhere.
- Less ambiguity. JSON has fewer ways to mean the same thing.
Where YAML wins
-
Human authoring. Less punctuation, fewer quotes, comments allowed. For files engineers edit by hand, YAML beats JSON every time.
-
Multi-document files. Kubernetes manifest YAML files often contain multiple documents separated by
---. JSON has no equivalent. -
References (anchors and aliases). Define a value once, reference it elsewhere — useful for repetitive config.
-
Comments. Self-documenting config is hard in JSON. YAML’s
#comments are ubiquitous in modern config files. -
Block scalars. Multi-line strings work cleanly:
description: | Line one. Line two.In JSON this becomes
"description": "Line one. Line two."— readable, but ugly.
YAML’s gotchas
YAML’s flexibility comes with footguns. The infamous ones:
The Norway problem
countries:
- GB
- NO # ← parses as boolean false in YAML 1.1
- FR
YAML 1.1 (still used by many parsers) treated NO, OFF, Y, N, YES, etc. as booleans. The fix: quote string values that look like booleans:
countries:
- GB
- "NO"
- FR
YAML 1.2 narrowed this — only true/false are booleans. But many parsers (PyYAML’s default loader, snakeyaml) still default to 1.1 behavior.
Indentation matters
foo:
bar: 1 # 2-space indent
baz: 2 # 3-space — ERROR
Tabs are typically forbidden. Mixing tabs and spaces is a runtime error in most parsers.
null has many spellings
a: # null
b: ~ # null
c: null # null
d: Null # null in 1.1, string in 1.2
Duplicate keys are silently overwritten
foo: 1
foo: 2 # most parsers: foo = 2, no error
JSON has the same issue, but YAML is used more in human-edited config where dupes are easier to introduce.
Performance
JSON parsing is roughly 5–10× faster than YAML in most languages. For a 1 MB config file:
- JSON: ~5 ms
- YAML: ~30–80 ms
Doesn’t matter for human-edited config (you parse it once at startup). Matters a lot for high-throughput data interchange.
When to use which
Is this a wire format between systems?
└── JSON
Is this human-edited configuration?
└── YAML
Is this a log line?
└── JSON (single-line, parseable)
Is this a Kubernetes / Helm / Ansible / GitHub Actions file?
└── YAML (no choice — those tools require it)
Is this OpenAPI / Swagger?
└── Either is accepted; YAML is more readable to hand-edit
Is this app state in a database?
└── JSON (or JSONB if you use Postgres)
Is this front matter (Hugo, Jekyll, Astro)?
└── YAML by default; JSON is also accepted
Are you not sure?
└── Default to JSON unless humans will hand-edit it
Hybrid: TOML
For human-edited config that wants JSON’s strictness without YAML’s whitespace sensitivity, TOML is the third option. It’s used by Cargo (Rust), Poetry (Python), and Hugo. Comments, types, no significant whitespace.
For most teams, picking JSON or YAML is enough — TOML is a niche addition.
Conversion
YAML → JSON is lossless for the data types JSON supports (objects, arrays, strings, numbers, booleans, null). YAML’s anchors/aliases get expanded; multi-document files become an array.
JSON → YAML is always lossless. Use the JSON → YAML converter on this site, or:
# yq (Go)
yq -o=yaml eval . data.json
# Python
python3 -c 'import json,yaml,sys; print(yaml.safe_dump(json.load(sys.stdin)))' < data.json
Common mistakes
- Hand-editing JSON. It’s possible, but YAML is almost always nicer for this. For a config file engineers will touch, prefer YAML.
- YAML in API responses. Don’t. Stick to JSON.
- Mixing tabs and spaces in YAML. Pick one (always spaces). Most parsers reject tabs entirely.
- Forgetting quotes around YAML 1.1 booleans. If your config could ever say
country: NO, quote it. - Trusting
yaml.load. The defaultloadfunction in PyYAML can deserialize Python objects, which is a code-execution hazard for untrusted input. Always useyaml.safe_load.
Try the tools
- JSON formatter — pretty-print and validate JSON
- JSON → YAML — instant converter
- JSON minifier — for the wire-format use case
- What is JSON? — fundamentals