JSON vs YAML: which config format to use, and how to convert safely
How JSON and YAML differ for config files, the Norway problem and indentation traps, and when to convert between them.
Published 2026-09-25
The same data, two different syntaxes
JSON and YAML both describe the same handful of data shapes — objects (maps), arrays (lists), strings, numbers, booleans and null. Any valid JSON document is also valid YAML, because YAML 1.2 was deliberately made a superset of JSON's data model (see the YAML 1.2.2 spec, §1.2). What differs is how much syntax you have to type to express structure.
JSON needs braces, brackets, commas and quotes around every key:
{
"service": "api",
"replicas": 3,
"env": ["staging", "prod"]
}
YAML expresses the same structure with indentation and a colon, no braces or quotes required:
service: api
replicas: 3
env:
- staging
- prod
Neither format is "better" in the abstract. JSON is unambiguous and trivial to parse in one pass, which is why it's the default for APIs and anywhere a machine writes the file. YAML is easier for a person to read and edit by hand, which is why it dominates Kubernetes manifests, GitHub Actions workflows, Docker Compose files and Ansible playbooks — files humans open and change directly.
What YAML gives up for readability
YAML's readability comes from inferring type and structure from whitespace and bare words, and that inference is also where most YAML bugs come from.
Indentation is structural, not cosmetic. In JSON, indentation is just formatting — {"a":{"b":1}} and a pretty-printed version parse identically. In YAML, indentation is the nesting. Mixing 2-space and 4-space indentation in the same file, or using a tab where the parser expects spaces, produces errors like "mapping values are not allowed here" that point at a line far from the actual mistake. Pick one indent width per file and keep it consistent.
Bare words get typed automatically — the "Norway problem." YAML 1.1 tried to be helpful by resolving unquoted words like yes, no, on, off, y, n, true and false (in various cases) as booleans. That's convenient until a config file has a country code: country: NO silently becomes the boolean false instead of the string "NO", breaking anything downstream that expected Norway's ISO code. This is widely known as the Norway problem.
YAML 1.2 fixed this at the spec level: the core schema only resolves true/True/TRUE and false/False/FALSE as booleans — yes, no, on and off are no longer special and stay strings. The catch is that several widely-used parsers (older PyYAML and libyaml-based libraries, for example) still implement YAML 1.1's looser resolution rules by default, so the bug still shows up in practice even though the spec no longer requires it. The safe habit, regardless of which parser reads the file: always quote a value that looks like it could be a boolean but is meant to be a string — "no", "yes", "NO", "on".
Numbers, nulls and other quiet gotchas
- Leading-zero strings.
zip: 02139in YAML without quotes is parsed as a number by YAML 1.2 parsers (the leading zero is lost), and a leading-zero value like0755is read as octal by YAML 1.1 parsers (it becomes 493). Quote postal codes, phone number fragments and version-like strings:zip: "02139". - Null has several spellings.
~,null,Null,NULL, and an empty value after a colon all resolve to null in YAML's core schema. JSON has exactly one:null. - Comments exist in YAML, not in JSON. YAML supports
# commentanywhere; JSON has no comment syntax at all, so anything hand-annotated in YAML loses those notes if you flatten it to JSON. - Anchors and aliases are YAML-only.
&name/*namelet a YAML file reuse a block without repeating it — useful for shared config blocks in CI files — but have no JSON equivalent, so converting YAML with anchors to JSON expands them into repeated, larger output. - Multi-document files. A single YAML file can hold several documents separated by
---. JSON has no equivalent; each JSON file is exactly one value.
When to actually convert
Converting JSON to YAML is always safe in the sense that it can't lose information — JSON's data model is a strict subset of YAML's, so every JSON document is already valid YAML and a converter just re-renders it with YAML's punctuation. Use the JSON to YAML converter when you have a package.json-style object or an API response and need to paste it into a Kubernetes manifest, a GitHub Actions workflow, or any other YAML-only config file.
Converting YAML to JSON is the direction where you can lose things: comments disappear (JSON has none to hold them), anchors/aliases get expanded, and if the file used YAML-1.1-style bare booleans anywhere, the resulting JSON will faithfully reproduce whatever the parser decided that value meant — which is exactly why validating the round trip matters. Use the YAML to JSON converter when a script or API needs a .yaml config file turned into a JSON object it can JSON.parse.
A practical workflow
- If you're hand-editing a YAML file, convert it to JSON and read the result: that shows exactly how the parser typed and nested every value (a list where you expected a map, a
falsewhere you meant"NO"). Use a JSON validator on JSON you hand-edit; it checks JSON syntax, not what the YAML meant. - Quote anything that isn't unambiguously a number: country codes, zip codes, version strings, and any word from the boolean list above (
yes,no,on,off,true,false, in any case). - Keep indentation consistent — pick 2 spaces (the common convention for Kubernetes and GitHub Actions) and don't mix it with tabs or a different width elsewhere in the same file.
- If the YAML file uses anchors or multiple
----separated documents, check the converted JSON by eye — the shape can change more than a simple key/value file would suggest.
Neither format is going away: JSON stays the right choice for anything a program writes and another program reads without a human in between, and YAML stays the right choice for anything a person edits directly. Knowing which quirks belong to which format is what keeps a five-minute config change from becoming an hour of debugging why a string turned into false.