Skip to content

Cookie settings

Optional analytics help us understand which pages and tools are useful. If you allow them, we use Google Analytics. Your files are never included, and every tool works the same if you decline. Cookie policy

JSON vs YAML vs TOML vs XML: Which Config and Data Format Should You Use?

The same config written in JSON, YAML, TOML and XML, a side-by-side comparison of comments, types and tooling, the gotchas that break each format, and how to validate them.

By Jasper Caldwell9 min read

JSON, YAML, TOML and XML can all describe the same nested data. They differ in who they were designed for: JSON for machines exchanging data, YAML and TOML for humans editing configuration, and XML for documents that need strict structure and validation. Picking the right one is mostly about who will read and write the file, and which ecosystem you are working in.

The same config in four formats

Here is a small service configuration: a few scalar settings, a list, a nested database section and a list of objects. Note the version 1.10 and the country code NO. Both are strings on purpose, and both cause trouble later.

JSON

{
  "name": "inventory-api",
  "version": "1.10",
  "debug": false,
  "port": 8080,
  "allowed_hosts": ["example.com", "api.example.com"],
  "database": {
    "host": "db.internal",
    "port": 5432,
    "pool_size": 10
  },
  "regions": [
    { "code": "NO", "name": "Norway" },
    { "code": "SE", "name": "Sweden" }
  ]
}

There is no way to add a comment, and every key needs double quotes.

YAML

# Service configuration
name: inventory-api
version: '1.10'
debug: false
port: 8080
allowed_hosts:
  - example.com
  - api.example.com
database:
  host: db.internal
  port: 5432
  pool_size: 10
regions:
  - code: 'NO'
    name: Norway
  - code: SE
    name: Sweden

YAML is the shortest of the four. Indentation is the structure, and the quotes around "1.10" and "NO" are doing real work, as explained below.

TOML

# Service configuration
name = "inventory-api"
version = "1.10"
debug = false
port = 8080
allowed_hosts = ["example.com", "api.example.com"]

[database]
host = "db.internal"
port = 5432
pool_size = 10

[[regions]]
code = "NO"
name = "Norway"

[[regions]]
code = "SE"
name = "Sweden"

[database] opens a table (an object), and [[regions]] adds one entry to an array of tables. Strings are always quoted, so there is no type guessing.

XML

<?xml version="1.0" encoding="UTF-8"?>
<!-- Service configuration -->
<service name="inventory-api" version="1.10">
  <debug>false</debug>
  <port>8080</port>
  <allowedHosts>
    <host>example.com</host>
    <host>api.example.com</host>
  </allowedHosts>
  <database host="db.internal" port="5432" poolSize="10"/>
  <regions>
    <region code="NO">Norway</region>
    <region code="SE">Sweden</region>
  </regions>
</service>

XML is the most verbose, and it forces a design choice the others do not: whether each value is an attribute or a child element. Without a schema, 8080 and false are just text.

Side-by-side comparison

JSONYAMLTOMLXML
CommentsNoYes (#)Yes (#)Yes (<!-- -->)
Built-in typesString, number, boolean, null, object, arraySame as JSON in the 1.2 core schema, plus anchors and tagsString, integer, float, boolean, four date/time types, array, tableText only, unless a schema (such as XSD) adds types
Structure shown byBraces and bracketsIndentationSection headers and dotted keysOpening and closing tags
Hand editingFiddly (quotes, commas)Easy to write, easy to get subtly wrongEasy for flat or moderately nested configVerbose
ToolingBuilt into nearly every languageWidely supported; behavior varies by parser versionGood and growingMature: schemas, XPath, XSLT
Common usesAPIs, package.json, data exchangeKubernetes manifests, CI workflows, Docker ComposeCargo.toml, pyproject.tomlMaven pom.xml, SVG, RSS, office document formats

When to choose each

  • JSON when machines produce and consume the data: API payloads, logs, data exports, anything sent between services. It is specified in RFC 8259, parsers are everywhere, and the strict grammar means few surprises.
  • YAML when humans edit deeply nested configuration and the tooling around you already expects it, as in Kubernetes or most CI systems. Use quotes generously.
  • TOML for application and project configuration that people edit by hand and that is mostly key/value pairs grouped into sections. Its explicit typing avoids YAML's guessing.
  • XML when you need mixed content (text with markup inside it), namespaces, or formal validation against a schema, or when an existing standard or toolchain is built on it.

If your ecosystem has already chosen, follow it: a Rust project uses Cargo.toml, and a GitHub Actions workflow is YAML.

Gotchas that break each format

YAML: implicit typing and the Norway problem

Unquoted YAML values are typed by pattern matching, and the rules changed between versions. Under YAML 1.1, the boolean type accepts y, yes, on, no, off and their capitalized forms, according to the YAML 1.1 boolean type definition. So this list of country codes:

countries:
  - GB
  - NO
  - SE

is read by a YAML 1.1 parser as ["GB", false, "SE"]. This is widely known as the "Norway problem". YAML 1.2 (the current specification, revision 1.2.2 published in 2021) changed the recommended core schema so that only true and false (in lower, title or upper case) are booleans. But many parsers and tools still apply 1.1 rules, so you cannot rely on the version.

Other implicit-typing traps:

  • version: 1.10 is a float and becomes 1.1. Quote version numbers.
  • 010 is octal (the number 8) under YAML 1.1. YAML 1.2 writes octal as 0o10.
  • port: 08080 is a string under YAML 1.1 rules (a leading zero with an 8 is neither octal nor decimal there) but the integer 8080 under the YAML 1.2 core schema.
  • An unquoted value containing : or starting with *, &, !, { or [ means something else to the parser.

Fix: quote any string that could look like a number, boolean, date or null. When in doubt, quote.

YAML: indentation

The YAML spec states that tab characters must not be used in indentation. Spaces only, and the depth must be consistent. The worse case is a key indented one level too deep or too shallow: that is often still valid YAML, it just attaches the key to a different parent, and nothing complains until the application misbehaves.

Fix: use an editor that shows whitespace, and look at the parsed structure, not just "valid/invalid". A YAML viewer displays the tree so a misplaced key stands out.

JSON: no comments and no trailing commas

The JSON grammar in RFC 8259 has no comment syntax, and a comma after the last item in an object or array is a syntax error:

{
  "debug": false,
  "port": 8080
}

That trailing comma after 8080 fails. Single-quoted strings and unquoted keys fail too, even though JavaScript accepts all three. Some tools accept a relaxed dialect (TypeScript's tsconfig.json allows comments, for example), but that is the tool's extension, not JSON.

Two quieter issues: RFC 8259 says object names "SHOULD be unique" rather than must, so a duplicated key may silently keep the first or last value depending on the parser. And it notes that good interoperability is achieved by expecting no more precision than IEEE 754 double precision, so large integers such as 64-bit IDs can lose digits. Send them as strings.

TOML: nesting and table order

TOML is strict where YAML is permissive, which is mostly good, but it has its own traps:

  • Keys after a table header belong to that table. In the example above, moving debug = false below [database] makes it database.debug. Put top-level keys first.
  • No redefinition. The TOML spec says defining a key more than once is invalid, and so is defining a table more than once.
  • Deep nesting gets noisy. Every level needs a full header such as [servers.alpha.limits]. For data nested five levels deep, YAML or JSON reads better.
  • Inline tables. In TOML 1.0, an inline table { ... } must stay on one line with no trailing comma. TOML 1.1.0, released in December 2025, allows newlines and a trailing comma, but parsers that only support 1.0 will reject them.
  • Backslashes in strings. Double-quoted strings process escapes, so "C:\Users" is an error. Use a single-quoted literal string: 'C:\Users'.

XML: verbosity, attributes and escaping

  • Attributes or elements? There is no single correct answer, and mixing styles makes documents harder to process. A common rule: attributes for simple metadata about an element, child elements for data that can repeat or contain structure.
  • Attributes are unordered and unique. The XML 1.0 specification says attribute order is not significant and an attribute name must not appear twice in the same tag.
  • Escaping. < and & must be escaped as &lt; and &amp; in text. An unescaped ampersand in a URL is one of the most common reasons an XML file fails to parse.
  • One root element, and -- is not allowed inside comments. Both are well-formedness rules.
  • Everything is text. <port>8080</port> is a string until a schema or your code says otherwise.

How to validate each format

Validation happens at two levels. Syntax checks that the file parses. Schema checks that the right keys exist with the right types. A file can pass the first and fail the second.

JSON. Paste it into the JSON Viewer to see it as a collapsible tree, with syntax errors reported where parsing stopped. On the command line, python -m json.tool config.json fails with a line and column on bad input. For structure, JSON Schema is the common choice.

YAML. The YAML Validator reports the line and column where parsing failed, and lets you switch between YAML 1.2 and 1.1 rules, so you can see whether NO would become false under the older rules. Exporting the parsed result as JSON shows exactly what type each value ended up as. For style rules (indentation width, trailing spaces), yamllint is a widely used linter.

TOML. The TOML Validator checks the file against the spec and shows the position of the first error. In Python 3.11 or later, the standard library can check it too:

import tomllib

with open("pyproject.toml", "rb") as f:
    tomllib.load(f)  # raises TOMLDecodeError with line and column

XML. Opening a file in the XML Viewer shows elements and attributes as a navigable tree, and malformed markup fails to load. The XML Beautifier re-indents single-line XML and reports errors. xmllint --noout file.xml checks well-formedness on the command line, and adding --schema schema.xsd validates against an XSD.

Check your file

If a config file is failing and you cannot see why, start with the validator for its format: the YAML Validator or TOML Validator for configuration, and the JSON Viewer or XML Viewer for data. They are free, need no account, and run in your browser, so a file containing credentials is never uploaded.

FAQ

Is YAML a superset of JSON?

Mostly. The YAML 1.2 specification says its primary focus was making YAML a strict superset of JSON, so most valid JSON parses as YAML 1.2. Edge cases exist, and YAML 1.1 parsers are not guaranteed to accept all JSON.

Can I put comments in JSON?

Not in standard JSON. Common workarounds are a "_comment" key, a sidecar README, or switching to a format that supports comments if humans maintain the file. Some tools accept a JSON-with-comments dialect, but other parsers will reject those files.

Why is TOML used for Python and Rust projects?

pyproject.toml and Cargo.toml are hand-edited files of mostly flat, sectioned settings, which is exactly what TOML handles well. Its explicit types avoid the implicit conversions that make YAML risky for version numbers.

Is XML obsolete?

No. JSON replaced it for many web APIs, but XML remains standard for SVG, RSS and Atom feeds, office document formats, many enterprise and publishing systems, and anywhere schema validation and mixed text-and-markup content matter.

Tools for this