BytePane

YAML vs JSON vs TOML for Config Files: Syntax Table

Data Formats6 min read

Quick answer

JSON is the interchange format: strict, no comments, universally parsed. YAML is the human-edited format for deeply nested documents (Compose, Kubernetes, CI) and, per the YAML 1.2 spec, a strict superset of JSON. TOML is the flat-tables format with unambiguous types and native dates, used by pyproject.toml and Cargo.toml. Pick the one your ecosystem already reads; the syntax differences below are what you will actually hit.

The same config in all three formats

A service config with a name, port, boolean, list of hosts, nested database section, and a release timestamp. The JSON and YAML samples were parsed with Node (js-yaml 4.1.1) and Python (json, PyYAML 5.4.1); the TOML sample was checked line by line against the TOML v1.0.0 specification.

JSON

{
  "name": "api-gateway",
  "port": 8080,
  "debug": false,
  "hosts": ["10.0.0.1", "10.0.0.2"],
  "database": {
    "url": "postgres://db:5432/app",
    "pool_size": 10
  },
  "released": "2026-09-17T10:00:00Z"
}

YAML

# Server settings
name: api-gateway
port: 8080
debug: false
hosts:
  - 10.0.0.1
  - 10.0.0.2
database:
  url: postgres://db:5432/app
  pool_size: 10
released: 2026-09-17T10:00:00Z

TOML

# Server settings
name = "api-gateway"
port = 8080
debug = false
hosts = ["10.0.0.1", "10.0.0.2"]
released = 2026-09-17T10:00:00Z

[database]
url = "postgres://db:5432/app"
pool_size = 10

One difference is visible immediately: in TOML, released must come before the [database] header, because every key after a table header belongs to that table. In JSON and YAML, order inside an object is free.

What each parser returns (tested)

Types are where the three formats diverge most. The date is a plain string in JSON but a real date object in YAML and TOML parsers:

// node yaml-test.js
const yaml = require('js-yaml')
const j = JSON.parse(fs.readFileSync('config.json', 'utf8'))
const y = yaml.load(fs.readFileSync('config.yaml', 'utf8'))
console.log(typeof j.released)              // string
console.log(y.released instanceof Date)     // true  -> 2026-09-17T10:00:00.000Z
console.log(JSON.stringify(yaml.load(fs.readFileSync('config.json', 'utf8'))) === JSON.stringify(j))
// true  -> the JSON file is also valid YAML
# python3
import json, yaml
j = json.load(open('config.json'))
y = yaml.safe_load(open('config.yaml'))
print(type(j['released']).__name__)   # str
print(type(y['released']).__name__)   # datetime  -> 2026-09-17 10:00:00+00:00

Per the Python docs, tomllib (3.11+) maps a TOML offset date-time to a timezone-aware datetime.datetime, a table to dict, and an array to list. It is read-only: there is no tomllib.dumps.

Syntax comparison table

FeatureJSONYAMLTOML
CommentsNone (RFC 8259)# line comment# line comment
NestingBraces, any depthIndentation (spaces only)[table] and [a.b.c] headers; inline { } tables
Lists[1, 2]- item per line, or [1, 2][1, 2] ; [[name]] for arrays of tables
StringsAlways double-quotedUnquoted, 'single', or "double""basic" (escapes) or 'literal' (no escapes)
Multi-line stringsOnly with \n escapes| keeps newlines, > folds them"""basic""" or '''literal'''
DatesStrings onlyTimestamp scalars (parser-dependent)Native RFC 3339 date-times, dates, times
Trailing commasNot allowedNot applicableAllowed in arrays, not in inline tables
Booleanstrue / falsetrue / false (YAML 1.1 also yes/no/on/off)true / false only
Superset relationSubset of YAML 1.2Designed as a strict superset of JSON (1.2)Independent format
Schema / toolingJSON Schema, ubiquitousJSON Schema via YAML editors, lintersTaplo, schema stores; fewer editors
Typical filespackage.json, tsconfig.json, API bodiesdocker-compose.yml, Kubernetes, GitHub Actionspyproject.toml, Cargo.toml, Hugo config

The YAML gotcha that TOML and JSON do not have

YAML guesses scalar types, and the guess depends on the parser's YAML version. The same three lines, run through PyYAML 5.4.1 (YAML 1.1 rules) and js-yaml 4 (YAML 1.2 rules):

country: NO      # PyYAML -> False      js-yaml -> 'NO'
version: 1.10    # PyYAML -> 1.1        js-yaml -> 1.1   (both: float)
time: 12:30      # PyYAML -> 750        js-yaml -> '12:30'

Quote anything that could look like a boolean, number, or time. JSON has no such ambiguity because every string is quoted, and TOML has none because its strings must be quoted and its dates follow RFC 3339.

Decision rule

  • Machine-to-machine or a tool already mandates it (package.json, API payloads, lockfiles): JSON.
  • Deep nesting edited by people (Compose services, Kubernetes manifests, CI workflows): YAML, with ambiguous scalars quoted.
  • Mostly flat sections with typed values and dates (project metadata, tool settings): TOML.

Primary sources: TOML v1.0.0 spec, YAML 1.2.2 spec, RFC 8259 (JSON), Python tomllib.

Frequently Asked Questions

Is YAML a superset of JSON?

By design, yes. The YAML 1.2 specification states that its primary focus was making YAML a strict superset of JSON, so a valid JSON document is also a valid YAML 1.2 document. In practice, older YAML 1.1 parsers (for example PyYAML) still differ on a few scalars such as unquoted NO or 12:30, so quote ambiguous strings.

Does JSON support comments?

No. RFC 8259 JSON has no comment syntax. YAML and TOML both use # for line comments. If you need comments in a JSON-shaped config, use YAML, TOML, or a JSON-with-comments dialect that your specific tool accepts (for example tsconfig.json), not plain JSON.

Which format should I pick for a new config file?

JSON when the file is written or read by programs and interoperability matters (package.json, API payloads). YAML when humans edit deeply nested documents and the ecosystem already uses it (Docker Compose, Kubernetes, GitHub Actions). TOML when the config is mostly flat key/value tables and you want unambiguous types and native dates (pyproject.toml, Cargo.toml).

Can Python read TOML without installing a package?

Yes, from Python 3.11 onward: the standard-library tomllib module parses TOML 1.0.0 with tomllib.load() and tomllib.loads(). It is read-only, so writing TOML still needs a third-party package. On Python 3.10 and older, tomllib does not exist.

Try it in the browser

Related guides