JSON vs YAML
Use JSON when machines are exchanging data and YAML when humans are editing configuration. JSON is unambiguous and tedious to write; YAML is comfortable to write and has enough ambiguity to cause genuine outages.
The same data in both
{
"service": "checkout",
"replicas": 3,
"ports": [8080, 8443],
"env": {
"LOG_LEVEL": "info",
"REGION": "eu-west-1"
}
}service: checkout
replicas: 3
ports:
- 8080
- 8443
env:
LOG_LEVEL: info
REGION: eu-west-1YAML is a superset of JSON, so the JSON version above is also valid YAML. The reverse is not true, which is why converting YAML to JSON always works and converting back never quite returns what you started with.
Compared
| JSON | YAML | |
|---|---|---|
| Comments | None | # to end of line |
| Punctuation | Heavy — braces, brackets, quotes, commas | Light — indentation carries structure |
| Whitespace | Insignificant | Significant, and tabs are forbidden |
| Multi-line strings | Escaped \n only | Block scalars with | and > |
| References | None | Anchors and aliases |
| Multiple documents per file | No | Yes, separated by --- |
| Type ambiguity | None — types are explicit | Real; see below |
| Parse speed | Very fast | Roughly an order of magnitude slower |
| Parser attack surface | Small | Larger — several CVEs historically |
| Typical use | APIs, logs, data interchange | Configuration, CI, infrastructure |
What YAML gives you that JSON does not
Comments
The decisive advantage for configuration. A config file nobody can annotate is a config file nobody can safely change.
# Raised from 2 during the March incident. Do not lower
# without checking the p99 latency dashboard first.
replicas: 3Anchors and aliases
Define a block once and reuse it. Useful for repeated CI job definitions, and the reason Helm charts and GitLab pipelines are readable at all.
defaults: &defaults
image: node:22
timeout: 600
test:
<<: *defaults
script: pnpm test
build:
<<: *defaults
script: pnpm buildMulti-line strings
script: |
set -euo pipefail
pnpm install
pnpm test
# In JSON the same thing is:
# "script": "set -euo pipefail\npnpm install\npnpm test"What YAML costs you
The Norway problem
YAML 1.1 treats several unquoted words as booleans. no becomes false, which memorably broke a list of country codes where Norway’s NO stopped being a string.
countries: [GB, FR, NO] # NO may parse as boolean false
enabled: yes # a boolean, not the string "yes"
version: 1.20 # a number — the trailing zero is gone
# Quote anything whose type matters:
countries: ["GB", "FR", "NO"]
version: "1.20"YAML 1.2 narrowed this to true and false, but many parsers still implement 1.1 behaviour, and you rarely control which parser reads your file.
Sexagesimal numbers
# A MAC address, or a build time, unquoted:
value: 12:30:00
# Some YAML 1.1 parsers read this as 45000 — base-60 seconds.Indentation is load-bearing
A key indented one space too far becomes a child of the previous key instead of its sibling. The file is still valid YAML — it just describes something different, and nothing will tell you until the behaviour is wrong.
# Intended: two sibling settings
database:
host: localhost
port: 5432
# Typo: port is now nested inside host
database:
host: localhost
port: 5432 # ← this one is at least an error
# Worse, because it is valid:
services:
web:
port: 80
cache: # one space short — silently a different structure
port: 6379Parsing YAML is not free
YAML’s grammar is large, and some parsers historically allowed a document to instantiate arbitrary objects. Python’s yaml.load was unsafe by default for years; yaml.safe_load is the correct call. Anchors also enable the "billion laughs" expansion, which is why serious parsers cap alias depth.
How to choose
- Machines exchanging data → JSON
- API payloads, logs, message queues, browser storage. Speed, unambiguous types and a small attack surface all matter here; comments do not.
- Humans editing configuration → YAML
- Kubernetes manifests, CI pipelines, Docker Compose, Ansible. Comments and readability matter more than parse speed, and the file is edited far more often than it is parsed.
- Both → write YAML, ship JSON
- Keep the annotated YAML in version control where people read it, convert it to JSON in the build, and let the runtime consume the unambiguous version.
Converting between them
YAML to JSON preserves every value — anchors are expanded and comments are dropped, but the data survives. JSON to YAML is lossless for practical documents, with two edge cases: integers beyond 2^53 lose precision, and YAML has no spelling for Infinity or NaN.
Convert YAML to JSONRuns in your browser.
Questions
Should I use JSON or YAML?
JSON when machines are exchanging data — APIs, logs, message queues — because it is fast, unambiguous and has a small attack surface. YAML when humans are editing configuration, because it supports comments and is far less punctuation to get wrong. A common pattern is to write YAML, keep it in version control, and convert it to JSON during the build.
Is YAML a superset of JSON?
Yes, since YAML 1.2. Any valid JSON document is valid YAML, so a YAML parser will read your JSON. The reverse does not hold: comments, anchors, block scalars and unquoted strings have no JSON equivalent.
What is the Norway problem in YAML?
YAML 1.1 treats several unquoted words as booleans, including no, yes, on and off. A list of country codes containing NO therefore ends up with false where Norway should be. Quoting any value whose type matters avoids it, and YAML 1.2 narrowed the rule to true and false — though many parsers still behave like 1.1.
Which is faster to parse, JSON or YAML?
JSON, by roughly an order of magnitude in most implementations. JSON’s grammar is tiny and parsers are heavily optimised; YAML’s is large and requires more lookahead. For configuration read once at startup the difference is irrelevant; for a hot path it is not.
Can I convert YAML to JSON without losing anything?
The data survives completely. The presentation does not: comments are dropped, anchors are expanded to their values, and formatting is lost. That is inherent to the conversion rather than a limitation of any particular tool.
Why does my YAML version number lose its trailing zero?
Because 1.20 unquoted is a number, and 1.20 and 1.2 are the same number. Quote it — "1.20" — whenever the string form is what you mean.