JSON1

Formats

JSON vs YAML

The two formats describe the same shapes β€” objects, arrays, strings, numbers, booleans, null. The differences that matter in practice are who writes the file, whether comments are allowed, and how aggressively the parser guesses what your unquoted text meant.

Which one to use

The useful split is not technical, it is about the author. JSON is what programs write and read: it is generated, transmitted, and parsed without a human in the loop. YAML is what people write by hand and then read again six months later.

That single question resolves most cases. An HTTP API returns JSON β€” YAML would buy nothing, and every client already has a parser. A CI pipeline, a Kubernetes manifest, or an app config is YAML, because a person maintains it and needs to leave notes explaining why a timeout is 45 seconds.

  • Wire format, machine-to-machine β€” JSON. Smaller, universally parsed, no ambiguity to resolve.
  • Config a human edits β€” YAML. Comments and less punctuation are the entire reason it exists.
  • Data you store and query β€” JSON. Databases index it; almost none index YAML.
  • Anything untrusted β€” JSON. Its grammar is tiny, which leaves far less to get wrong.

What actually differs

YAML 1.2 is a superset of JSON: any valid JSON document is also valid YAML. The reverse is not true, and the gaps are where conversion loses things.

Behaviour that changes when you move a document between the two.
JSONYAML
CommentsNot allowed at all# to end of line
StructureBraces and bracketsIndentation, or braces
Quoting stringsAlways requiredOptional, which is the catch
Trailing commasRejectedNot applicable
Duplicate keysLast one wins, silentlyAn error in strict parsers
Multi-line strings\n escapes only| and > blocks
Anchors and reuseNone&anchor and *ref

Converting JSON to YAML

A nested payload converts cleanly, and the result is genuinely shorter β€” the punctuation JSON needs is carried by indentation instead. Arrays sit at their parent's indent level, which is the common house style and what most linters expect.

Note what happened to env: an array of objects becomes a list of dashes, with each object's first key inlined on the dash and the rest indented under it. That is the shape Kubernetes and GitHub Actions both use.

Input JSON

{
  "service": "api",
  "replicas": 3,
  "ports": [8080, 8443],
  "env": [
    { "name": "LOG_LEVEL", "value": "debug" },
    { "name": "REGION", "value": "eu" }
  ],
  "limits": { "cpu": "500m", "memory": "512Mi" }
}

Output YAML

service: api
replicas: 3
ports:
- 8080
- 8443
env:
- name: LOG_LEVEL
  value: debug
- name: REGION
  value: eu
limits:
  cpu: 500m
  memory: 512Mi
Real output from the /json-to-yaml/ converter. Empty objects and arrays stay visible as {} and [] rather than becoming blank lines you cannot see.

The quoting trap

This is the one that costs real time, and it only bites in the YAML direction. In JSON, "01234" is unmistakably a string, because strings are always quoted. In YAML, quotes are optional β€” so a parser has to guess, and it guesses from the characters.

Converting out of JSON is safe, because the tool knows what it started with. Anything that would be re-read as a number or a boolean gets quoted on the way out:

Input JSON

{
  "zip": "01234",
  "version": "1.10",
  "enabled": "yes"
}

Output YAML

zip: "01234"
version: "1.10"
enabled: "yes"
The quotes are not decoration. Drop them and the next parser reads different values.

Why hand-written YAML loses data

Now the same three fields typed by hand, without quotes. This is the failure people hit, and nothing warns them:

  • Leading zeros β€” postcodes, phone numbers, and account IDs all lose them. 01234 becomes 1234.
  • Trailing zeros β€” 1.10 becomes 1.1, so a version string stops matching.
  • `yes` and `no` β€” our parser keeps these as strings, matching YAML 1.2. Older 1.1 parsers, including PyYAML's default, turn no into false. This is the Norway problem: the country code NO becomes a boolean.

Hand-written YAML

zip: 01234
version: 1.10
enabled: yes

Parsed as JSON

{
  "zip": 1234,
  "version": 1.1,
  "enabled": "yes"
}
A leading zero is gone, 1.10 became 1.1, and a postcode is now arithmetic. All three are real output from /yaml-to-json/.

What survives a round trip

JSON to YAML and back is lossless, because the quoting is written for you. Hand-written YAML to JSON and back is not, and the loss happens on the way in, before any conversion runs.

So the safe habit is one rule: quote every string in YAML that could be read as something else. Numbers stored as text are the whole category β€” identifiers, versions, postcodes, country codes, anything with a leading zero.

  • Comments do not survive in either direction. JSON has nowhere to put them, so a conversion drops every # line.
  • Anchors and aliases are expanded, not preserved β€” reuse becomes repetition.
  • Key order is kept, which matters more for reviewing a diff than for correctness.