Skip to main content

YAML vs JSON

YAML 1.2.2 (2021) is defined such that every valid JSON document is valid YAML – YAML is a strict superset. This means the data model is identical: both support strings, numbers, booleans, null, arrays (sequences), and objects (mappings). The key difference is intended authoring context. YAML was designed for humans writing configuration files by hand. It uses indentation instead of braces, allows comments, supports multi-line strings natively, and provides anchors/aliases for deduplication – features that significantly reduce repetition in large Kubernetes manifests or CI/CD pipeline definitions. JSON was designed for machine-generated data exchange. It is strict, unambiguous, and fast to parse. There are no implicit type coercions, no comments, and no alternative syntax forms. This strictness makes JSON safer for machine-to-machine communication where subtle ambiguity could cause bugs. The Norway Problem: YAML 1.1 (still used by many parsers) treats yes/no/on/off and country codes like NO, FI as booleans. YAML 1.2 fixed this – only true and false are booleans. But PyYAML used 1.1 behavior until version 6.0 (2021). This ambiguity has caused production bugs in Kubernetes configurations and Ansible playbooks.

YAML is a strict superset of JSON (YAML 1.2) designed for human-authored configuration files. JSON is the standard wire format for APIs and machine-generated data. YAML adds comments, multi-line strings, anchors for deduplication, and indentation-based structure. JSON is simpler, faster to parse, and has no ambiguity traps. Kubernetes manifests, GitHub Actions, and Docker Compose use YAML; REST APIs and webhook payloads use JSON.

FeatureYAMLJSON
Superset relationshipYAML 1.2 is a strict superset of JSONJSON is the subset
Specificationyaml.org 1.2.2 spec (2021) – community standardRFC 8259 (IETF), ECMA-404 – formal standards
CommentsYes – # to end of lineNo – JSON has no comment syntax
Multi-line stringsYes – literal | and folded > block scalarsNo – only escaped \n within a string value
Anchors / aliasesYes – & anchor, * alias, << merge keyNo – repetition required
Indentation syntaxBlock style uses indentation (spaces only)Braces and brackets – indentation is optional whitespace
Type ambiguityYes – YAML 1.1 yes/no/on/off coerced to boolNo – true/false only, strict and unambiguous
Parse speedSlower – complex grammar, multiple stylesFaster – simple grammar, unambiguous tokenization
Tabs in indentationForbidden – spaces onlyAllowed as insignificant whitespace
Multiple documentsYes – --- separator between docs in one fileNo – one JSON value per document
Trailing commasN/A – no comma syntax in block styleNot allowed – [1, 2, 3,] is invalid
Primary use caseConfig files (k8s, GitHub Actions, Docker Compose, Ansible)API payloads, webhooks, machine-generated data

When to use YAML

YAML is correct for: configuration files that humans write and maintain by hand, especially when the file is large (Kubernetes Deployments, Helm charts, GitHub Actions workflows). YAML's comments let you document configuration intent. Anchors eliminate repetition across similar resources. If the file is read mostly by machines but written mostly by humans – use YAML.

When to use JSON

JSON is correct for: API responses, webhook payloads, event streams, and any data primarily produced and consumed by code rather than humans. JSON's strict grammar means no ambiguous coercions, no parser version surprises, and native support in every language runtime and HTTP client. For any inter-service communication or external API – use JSON.

Common Mistakes

  • Using YAML for API responses – YAML's slower parser, lack of native browser support, and ambiguity traps make it a poor choice for machine-to-machine data exchange. JSON is the right choice for HTTP APIs.
  • Relying on PyYAML < 6.0 without specifying yaml.safe_load() – PyYAML 5.x and earlier use YAML 1.1 behavior where yes/no/on/off resolve to booleans. This has caused real production bugs in Kubernetes manifests. Always use yaml.safe_load() and upgrade to PyYAML 6.0+ for YAML 1.2 behavior.
  • Using tabs for YAML indentation – tabs are explicitly forbidden as indentation in YAML. Editors set to use tabs for Python or Makefile files will produce parse errors in YAML files. Configure your editor to use spaces for YAML files.
  • Not quoting YAML values that look like other types – the string 'true' must be written as '"true"' or !!str true to avoid being parsed as boolean. Port numbers like 8080 in a string context must be quoted if you need them as strings. Always quote ambiguous values.
  • Forgetting that JSON in a YAML file is valid – if you have a complex JSON structure you want to embed, YAML accepts it verbatim. You do not need to convert JSON to YAML block style. This is useful for embedding JSON payloads in Kubernetes ConfigMaps or GitHub Actions workflow inputs.

FAQ

Is YAML always a superset of JSON?

YAML 1.2 is a strict superset of JSON – any valid JSON document is valid YAML 1.2. YAML 1.1 is not a strict superset because it treats some JSON-valid constructs differently (e.g., JSON true/false is fine, but YAML 1.1 also accepts Yes/No which JSON does not produce). Most practical JSON documents are valid under both YAML versions.

What is the Norway Problem?

In YAML 1.1, the two-letter country code NO was parsed as boolean false (short for 'no'). Similarly, FI, SE, and other codes were silently coerced. This caused bugs in Ansible playbooks and Kubernetes configurations where country codes or values like 'on'/'off' appeared as strings but were parsed as booleans. YAML 1.2 fixed this – only the lowercase literals true and false are booleans.

Can YAML replace JSON in REST APIs?

Technically yes – servers can serve YAML via Content-Type: application/yaml (RFC 9512, 2024). In practice, no. Browsers have no native YAML parser. YAML parsing is slower and more complex than JSON parsing. Third-party API consumers expect JSON. Use YAML for configuration files, use JSON for API responses.