JSON vs YAML
One is built for machines to exchange, the other for humans to edit. Choosing correctly is mostly a question of who is doing the typing.
The Same Data, Twice
// JSON
{
"name": "reporting-service",
"port": 8080,
"features": { "exportToCsv": true },
"replicas": ["db-r1.internal", "db-r2.internal"]
}# YAML
name: reporting-service
port: 8080
features:
exportToCsv: true
replicas:
- db-r1.internal
- db-r2.internalYAML drops the braces, brackets and quotes, using indentation to convey structure instead. It is unquestionably nicer to type and to read, and it supports comments — which JSON does not. For a file a person maintains by hand, those are real advantages.
They come at a price, and the price is ambiguity.
Head to Head
| JSON | YAML | |
|---|---|---|
| Primary reader | Machines | Humans |
| Comments | No | Yes |
| Whitespace | Insignificant | Structural — indentation is the syntax |
| Specification size | ~16 pages | ~80 pages |
| Parse speed | Fast | Considerably slower |
| Type inference | None — types are explicit | Aggressive, and occasionally wrong |
| Reuse / references | No | Anchors and aliases |
| Safe on untrusted input | Yes | Only with a safe loader |
The Norway Problem
YAML infers types from unquoted values, and its rules are broader than most people expect. The canonical example:
countries:
- GB
- IN
- NO # ← parses as the boolean false, not the string "NO"Under YAML 1.1, no, NO, off, n and false are all boolean false, while yes, on and y are true. So a list of ISO country codes silently drops Norway and replaces it with false. This bug is common enough to have a name.
It is not the only case:
version: 1.20 # → the number 1.2, trailing zero lost
time: 12:30 # → 750 under YAML 1.1 (sexagesimal!)
zip: 01234 # → may parse as octal, or as 1234
build: 1e5 # → the number 100000, not the string "1e5"YAML 1.2 narrowed the boolean rules considerably, but many widely used parsers still implement 1.1 semantics. The defence is simple and worth applying unconditionally: quote any string that could be mistaken for something else — version numbers, country codes, postcodes, times, anything with leading zeros.
JSON has no equivalent hazard, because types are explicit. "NO" is a string and NO is a syntax error; there is no third option where it quietly becomes a boolean.
Indentation as Syntax
In YAML, whitespace is the structure. A misplaced space changes what the document means, and there are no closing delimiters to make the error visible:
server:
host: 0.0.0.0
port: 8080 # ← one extra space: a parse error, or worse, a nested keyTabs make this worse — YAML forbids them for indentation entirely, so an editor configured to insert tabs produces files that will not parse at all. In JSON, indentation is decoration: you can reformat freely and the meaning is unchanged, which is precisely why formatting and minifying are safe operations.
Security
The more serious difference. Some YAML parsers historically supported constructing arbitrary language objects from tags in the document, which meant loading an untrusted file could execute code:
# Dangerous with an unsafe loader
!!python/object/apply:os.system ["rm -rf /"]# Python — always use safe_load for anything you did not write
import yaml
yaml.safe_load(text) # correct
yaml.load(text) # historically unsafe; requires an explicit Loader nowModern libraries default to safe behaviour, and PyYAML now requires an explicit loader argument. But the capability exists in the format's design, and the safe function is a separate call you have to remember.
JSON.parse has no comparable surface. It can only ever produce data — never behaviour. The residual concerns with JSON, covered on the parser page, are resource exhaustion and prototype pollution during a subsequent merge, both of which are milder and easier to guard.
What YAML Offers That JSON Does Not
Two features genuinely worth having in configuration, with no JSON equivalent.
Anchors and aliases, for reusing a block:
defaults: &defaults
timeoutMs: 30000
retries: 3
production:
<<: *defaults
host: prod.example.com
staging:
<<: *defaults
host: staging.example.comMulti-line strings, without escaping every newline:
description: |
This preserves
line breaks exactly.
summary: >
This folds the lines
into a single paragraph.The JSON version of that second block is "This preserves\nline breaks exactly.\n" — correct, but unpleasant to write or review by hand.
Choosing
Use JSON when
- Data is exchanged between programs
- It is an HTTP API request or response
- The producer or consumer is a machine
- Parse speed matters
- The input may be untrusted
Use YAML when
- Humans write and edit the file
- Comments explain non-obvious settings
- It lives in version control and gets reviewed
- Repeated blocks benefit from anchors
- Multi-line text is common
In practice most systems use both, and correctly so: YAML for the CI pipeline, the Kubernetes manifests and the application config that engineers edit; JSON for what the services actually send each other at runtime. Since JSON is valid YAML, a YAML parser can read both — which makes YAML a convenient input format and JSON the sensible output one.
Frequently Asked Questions
Is JSON valid YAML?
Yes. YAML 1.2 was defined as a strict superset of JSON, so any valid JSON document is also valid YAML and most YAML parsers will read it. The reverse is not true — YAML's indentation-based syntax, anchors and multi-document files have no JSON equivalent.
Why is YAML considered risky?
Two reasons. Its type inference produces surprises — the classic being unquoted no becoming the boolean false, which broke country-code lists for years. And some parsers historically supported constructing arbitrary objects from a document, which made loading untrusted YAML equivalent to code execution. Always use a safe-loading function.
Which is faster to parse?
JSON, substantially. Its grammar is tiny and unambiguous, so parsers are simple and fast. YAML's specification is far larger, with indentation tracking, multiple scalar styles, anchors and type inference all to handle.
Should my API return YAML?
Almost never. JSON is the expected format for HTTP APIs — every client library supports it, it parses faster, and its lack of ambiguity matters more when a machine is the only reader. Keep YAML for files humans write and edit.