{ jsonzen }0 bytes uploaded
2026-07-07

JSON vs YAML: differences, gotchas, and when to use each

A practical comparison of JSON and YAML — syntax, data types, comments, the Norway problem, and a clear rule for choosing between them.


title: "JSON vs YAML: differences, gotchas, and when to use each" slug: "json-vs-yaml" date: "2026-07-07" description: "A practical comparison of JSON and YAML — syntax, data types, comments, the Norway problem, and a clear rule for choosing between them." keywords:

  • json vs yaml
  • yaml vs json
  • difference between json and yaml
  • convert json to yaml

JSON and YAML describe the same data model — maps, sequences, scalars — with opposite philosophies. JSON optimizes for machines: minimal grammar, unambiguous parsing, miserable to write by hand. YAML optimizes for humans: readable, commentable, and carrying enough syntactic cleverness to hurt you. Here's the practical comparison, ending with a rule of thumb that settles almost every case.

The same data, side by side

{
  "service": "api",
  "replicas": 3,
  "env": ["staging", "prod"],
  "resources": {"cpu": "500m", "memory": "1Gi"}
}
service: api
replicas: 3
env:
  - staging
  - prod
resources:
  cpu: 500m
  memory: 1Gi

YAML drops the braces, brackets, and most of the quotes, and replaces them with significant indentation. For a config file a human maintains, that's a real readability win. For data a program emits and another program consumes, it buys nothing and costs parsing complexity.

Where they actually differ

Comments. YAML has them (# like this); JSON famously does not. This single feature explains most of YAML's dominance in config files. (If you've inherited JSON with // comments in it, it isn't valid JSON — the JSON Repair tool strips them.)

Quoting and typing. In JSON, strings are always quoted, so there's no ambiguity about what's a string. In YAML, unquoted scalars are guessed: 3 becomes a number, true a boolean, null a null. The guesses are usually right — and catastrophic when wrong (next section).

Multi-line strings. YAML's | (literal block) and > (folded block) make embedded scripts and certificates pleasant. JSON forces everything onto one line with \n escapes.

Anchors and references. YAML can define a block once (&defaults) and reuse it (<<: *defaults) — powerful for de-duplicating config, and a rich source of confusion and even denial-of-service risk (the "billion laughs" expansion) in untrusted input. JSON has no references: what you read is what you get.

Grammar size. JSON's entire spec fits on a postcard, every parser agrees on it, and JSON.parse is built into every runtime. YAML's spec is enormous; parsers disagree on edge cases, and YAML 1.1 vs 1.2 differences are still biting people a decade later.

A detail that surprises people: YAML 1.2 is a superset of JSON — every valid JSON document is valid YAML. Kubernetes accepting JSON manifests isn't a special feature; it's just YAML parsing.

The gotchas that made YAML famous

The Norway problem. In YAML 1.1, unquoted no parses as boolean false. A country list [se, no, dk] becomes ["se", false, "dk"]. Same for yes, on, off, and friends. YAML 1.2 fixed this, but plenty of parsers still default to 1.1 behavior.

The version-number problem. version: 1.20 parses as the float 1.2. version: "1.20" stays a string. Software versions, ZIP codes, and Git SHAs consisting only of digits all need quotes.

Sexagesimal surprise. In YAML 1.1, 1:30 can parse as 90 (base-60!). Time-like values need quotes too.

Tabs. YAML forbids tabs in indentation. A single tab pasted in from a different editor produces an error message that rarely says "tab".

JSON's gotchas, by contrast, are boring: no trailing commas, no comments, double quotes only. When you hit one, the parser tells you where — and the JSON Validator turns that position into a line and column you can act on.

When to use which

The rule of thumb:

  • Humans write it, humans maintain it → YAML. CI pipelines, Kubernetes manifests, docker-compose, app config. Comments alone justify the choice.
  • Machines write it, machines read it → JSON. APIs, data interchange, storage, logs, anything generated or parsed programmatically. Unambiguous, universal, fast.
  • Untrusted input → JSON. Smaller attack surface, no reference expansion, no type guessing.

The formats are lossless to convert between in the JSON→YAML direction, and almost lossless the other way (comments and anchors don't survive). When you need to switch — say, embedding a YAML config's data in an API payload, or making a JSON export human-editable — the JSON ↔ YAML converter does both directions in your browser, with nothing uploaded. For a final readability pass on the JSON side, the JSON Formatter pretty-prints the result.