Skip to content
OneKitTools logoOneKitTools
dev5 min read

"JSON vs YAML vs TOML: Which Config Format Should You Use?"

JSON, YAML and TOML compared side by side — strengths, pitfalls like the Norway problem, a decision table, and how to convert between formats safely.

OneKitTools TeamJuly 10, 2026

JSON vs YAML vs TOML: The Same Config, Three Ways

Every project eventually grows a config file, and every team eventually argues about its format. The debate is worth having once, properly — because the three main contenders make genuinely different trade-offs, and the wrong pick costs you in either readability, tooling, or 3 a.m. debugging sessions caused by an invisible indentation error.

Here's the same configuration in all three formats.

JSON:

{
  "name": "my-service",
  "port": 8080,
  "debug": false,
  "database": {
    "host": "db.internal",
    "replicas": ["eu-1", "eu-2"]
  }
}

YAML:

name: my-service
port: 8080
debug: false
database:
  host: db.internal
  replicas:
    - eu-1
    - eu-2

TOML:

name = "my-service"
port = 8080
debug = false

[database]
host = "db.internal"
replicas = ["eu-1", "eu-2"]

Same data, three philosophies. Let's break down where each one wins and where each one bites.

JSON: The Universal Interchange Format

JSON is the least ambiguous of the three. Strings are always quoted, structure is always explicit, and every language ships a parser in its standard library. It's the native format of web APIs, and the strictness that makes it verbose also makes it nearly impossible to misread.

Strengths:

  • Universal. If a tool reads config at all, it reads JSON.
  • Strict grammar. One canonical way to write everything — no "did you mean the string or the boolean?" ambiguity.
  • Machine-friendly. Trivial to generate, parse, diff, and validate with JSON Schema.

Weaknesses:

  • No comments. This alone disqualifies it for hand-maintained config. Workarounds like "_comment" keys are hacks (JSONC and JSON5 fix this, but support is spotty).
  • Punctuation tax. Quotes on every key, commas everywhere — and a trailing comma is a syntax error, the most common JSON mistake in existence.
  • No native dates, no multi-line strings without \n escapes.

When a hand-edited JSON file breaks, the error message is rarely helpful. Running it through a JSON Formatter pinpoints the exact character where parsing fails — usually a trailing comma or a stray unquoted key.

YAML: Readable, Powerful, and Full of Traps

YAML is what you get when you optimize purely for human reading. No braces, no quotes required, comments with #, and structure expressed through indentation. It's the lingua franca of ops: Kubernetes, Docker Compose, GitHub Actions, Ansible.

Strengths:

  • Cleanest to read for deeply nested data.
  • Comments, anchors/aliases for reuse, multi-line strings with | and >.
  • Superset of JSON — any valid JSON is valid YAML.

Weaknesses — and these are famous:

  • The Norway problem. In YAML 1.1 (which many parsers still implement), unquoted no parses as boolean false. So a country list becomes:
countries: [GB, FR, NO]   # NO → false. Sorry, Norway.

The same trap hits yes, on, off, and y.

  • The version-number problem. version: 1.20 parses as the float 1.2, silently eating your zero. version: "1.20" is what you meant.
  • Whitespace is structure. One space of mis-indentation changes the meaning of the document without any syntax error. Tabs are forbidden, but your editor may insert them anyway.
  • The spec is huge. Nine ways to write a multi-line string; implicit typing rules that vary between parsers.

The practical rule for YAML: quote anything that isn't obviously a string, a number you want as a number, or a real boolean. When in doubt, quote.

TOML: Explicit by Design

TOML ("Tom's Obvious, Minimal Language") was designed as a reaction to both: keep YAML's comments and readability, keep JSON's unambiguity. Strings are always quoted, booleans are only true/false, and it has first-class dates. It's the format of Rust's Cargo.toml and Python's pyproject.toml.

[package]
name = "my-app"
version = "1.20"        # string? no — quote it: "1.20". Bare 1.20 is a float.
release = 2026-07-10    # native date type, no quotes needed

[dependencies]
serde = { version = "1.0", features = ["derive"] }

Strengths: comments, no indentation semantics, no implicit type coercion surprises, great for flat-to-moderately-nested config.

Weaknesses: deep nesting gets awkward ([server.http.limits.upload] headers repeat the full path), arrays of tables ([[products]]) confuse newcomers, and adoption outside the Rust/Python ecosystems is thinner. A TOML Editor with live validation helps when you're past two levels of nesting.

Head-to-Head Comparison

CriterionJSONYAMLTOML
Comments
Ambiguity riskNoneHigh (implicit typing)Low
Whitespace-sensitiveNoYesNo
Native datesPartial
Deep nestingFineBestAwkward
Hand-editing comfortPoorGoodGood
Ubiquity of parsersTotalHighGrowing
Typical homeAPIs, lockfilesCI/CD, KubernetesCargo, pyproject

So Which One Should You Pick?

  • Machine-to-machine data (APIs, caches, lockfiles): JSON. No contest — nobody hand-edits it, so its weaknesses don't matter.
  • Hand-maintained app/project config: TOML. Comments plus zero ambiguity is exactly the trade-off you want for files humans edit and machines trust.
  • The ecosystem already chose: YAML. You don't get to pick the format for Kubernetes or GitHub Actions. Learn the quoting rules and lint aggressively.
  • A word on XML: you'll still meet it in Maven, Android manifests, and enterprise SOAP land. It's not wrong, just verbose and legacy-flavored — if you inherit one, an XML Formatter makes it navigable.

One more tip for TypeScript projects: whatever format your config lives in, generate types from a sample with JSON to TypeScript so your code stops guessing what's inside.

Converting Between Formats

Since JSON, YAML and (mostly) TOML represent the same data model — maps, arrays, scalars — conversion is usually lossless in the JSON→YAML direction and almost lossless the other way (comments die in translation, and YAML anchors get expanded). This makes a converter the fastest way to migrate config or to sanity-check what a YAML parser actually sees: convert to JSON, and all the implicit typing becomes explicit. If NO comes back as false, you've found your bug before production did.

Convert Your Config Now

Paste any config into the JSON ⇄ YAML converter to translate between formats instantly and reveal hidden type surprises. No account required.

Share