YAML Cheatsheet
JSON Compatibility
Use this YAML reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Every JSON document is valid YAML 1.2
YAML 1.2 was rewritten specifically to make JSON a strict subset. A .json file
can be renamed .yaml and parsed unchanged.
{"name": "api", "replicas": 3, "tags": ["web", "prod"],
"limits": {"cpu": "500m"}, "debug": false, "extra": null}Feed that exact text to yaml.safe_load and you get the same Python object
json.loads gives, == and all. That is because JSON is just YAML flow style with
mandatory quoting and no trailing commas.
JSON pasted inside YAML
Since flow style is legal anywhere a value is, a JSON blob drops straight into a YAML file with no reformatting.
service: api
config: {"retries": 3, "hosts": ["a", "b"], "debug": false}
annotations:
checksum/config: abc123
scheduler.alpha.kubernetes.io/tolerations: |
[{"key": "dedicated", "operator": "Equal", "value": "gpu"}]config parses to {'retries': 3, 'hosts': ['a', 'b'], 'debug': False}. The
tolerations line is different on purpose: that annotation's value must be a
string containing JSON, so it goes in a | block. Kubernetes annotations are
map[string]string, so any structured value has to be stringified.
The flow-style equivalence
| YAML block | YAML flow | JSON |
|---|---|---|
a: 1 | {a: 1} | {"a": 1} |
a: - 1 - 2 | {a: [1, 2]} | {"a": [1, 2]} |
a: b: 1 | {a: {b: 1}} | {"a": {"b": 1}} |
a: | {a: null} | {"a": null} |
| (none) | [] | [] |
What YAML relaxes on top of JSON:
- Keys need no quotes.
- Trailing commas are allowed:
[1, 2, 3,]. - Comments are allowed inside flow collections.
null,~, and an omitted value are all null.
What YAML adds that JSON has no answer for
| Feature | YAML | JSON |
|---|---|---|
| comments | # | none |
| multi-line strings | | and > blocks | "a\nb" only |
| references | &a / *a | none |
| inheritance | <<: *base | none |
| several docs in one file | --- | none, or NDJSON |
| dates as a type | 2024-01-15 | string only |
| non-string keys | 1: x | keys are always strings |
| explicit types | !!str 42 | none |
| trailing commas | allowed | forbidden |
NaN and Infinity | .nan, .inf | not in the spec |
| indentation matters | yes | no |
Converting between them
# YAML -> JSON yq -o=json '.' config.yaml python -c "import yaml,json,sys; \ print(json.dumps(yaml.safe_load(sys.stdin), default=str))" < config.yaml # JSON -> YAML yq -P '.' data.json python -c "import yaml,json,sys; \ print(yaml.safe_dump(json.load(sys.stdin), sort_keys=False))" < data.json # every doc in a multi-doc stream, as one JSON array yq -o=json eval-all '[.]' manifests.yaml
Two things break a naive YAML-to-JSON conversion:
- Dates. 2024-01-15 loads as a datetime.date, which json.dumps refuses
with TypeError: Object of type date is not JSON serializable. Hence
default=str above.
- Non-string keys. {1: "x"} or {True: "x"} becomes {"1": "x"} in JSON,
silently changing the key's type. A round trip is not lossless.
Also lost in a round trip: comments, anchors, the choice of block versus flow
style, and key order unless you pass sort_keys=False.
Choosing between them
| Use | Format |
|---|---|
| human-edited config | YAML or TOML |
| API request and response bodies | JSON |
| machine-to-machine | JSON |
| a config with repeated blocks | YAML, for <<: *base |
| a config a program writes back out | JSON, no comments to lose |
| logs, one record per line | NDJSON |
| something untrusted users submit | JSON, far smaller attack surface |
YAML's superset relationship means a JSON parser is the safest way to read a file
you know is JSON. Reading it with a YAML parser adds the whole type resolver, the
alias expander, and, if you slip and call yaml.load, arbitrary object
construction. Use the narrower parser when you can.