fileview.dev

JSON vs YAML

10 min read

Written while building a viewer that parses both, which means every ambiguity below is one that showed up in a real file someone opened.

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"
  }
}
JSON
service: checkout
replicas: 3
ports:
  - 8080
  - 8443
env:
  LOG_LEVEL: info
  REGION: eu-west-1
YAML — the same document

YAML 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

JSONYAML
CommentsNone# to end of line
PunctuationHeavy — braces, brackets, quotes, commasLight — indentation carries structure
WhitespaceInsignificantSignificant, and tabs are forbidden
Multi-line stringsEscaped \n onlyBlock scalars with | and >
ReferencesNoneAnchors and aliases
Multiple documents per fileNoYes, separated by ---
Type ambiguityNone — types are explicitReal; see below
Parse speedVery fastRoughly an order of magnitude slower
Parser attack surfaceSmallLarger — several CVEs historically
Typical useAPIs, logs, data interchangeConfiguration, 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: 3

Anchors 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 build

Multi-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: 6379

Parsing 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.