YAML, TOML, INI, XML, and JSON: converting configuration safely
How the common configuration formats differ in types and structure, and how to convert between them without silent changes.
A single project can easily use five configuration formats: JSON for package.json, YAML for CI pipelines and Docker Compose, TOML for pyproject.toml or Cargo.toml, INI for a legacy service, and XML for a Java or .NET build. They all describe nested keys and values, but each has different rules about types, comments, and structure. Converting between them is safe once you know where those rules differ.
The formats at a glance
| Format | Comments | Types | Typical use |
|---|---|---|---|
| JSON | No | string, number, boolean, null, array, object | APIs, package manifests, tool configs |
| YAML | Yes (#) | JSON types plus dates, anchors, and more, depending on the parser | Kubernetes, Compose, GitHub Actions, Ansible |
| TOML | Yes (#) | JSON types plus dates and times | Rust, Python packaging, Hugo, Netlify |
| INI | Yes (; or #) | Strings only (interpretation varies) | php.ini, Windows apps, Git config |
| XML | Yes | Text only, unless a schema defines types | Maven, .NET, Android, feeds, sitemaps |
JSON is the common denominator: every other format can be converted to it, and it is what most programs work with internally. The converters on this site therefore all go to or from JSON.
YAML: readable, but full of surprises
YAML’s lack of quotes makes it pleasant to read, but it means the parser has to guess types. In YAML 1.1, which is still used by PyYAML and older Go and Ruby libraries, these unquoted values are not strings:
NO,no,off, andnbecome false. That is the “Norway problem” in country-code lists.1.10becomes the number 1.1, and version strings lose their trailing zero.010may become 8 (octal), and0x1Fbecomes 31.2026-09-24becomes a date object, and12:30may become 750 (base-60 arithmetic).
The fix is to quote any value that must stay a string. When you convert JSON to YAML with JSON to YAML, strings like these are quoted automatically, so the file reads the same in every parser. Converting in the other direction with YAML to JSON shows exactly which type each value was given. That is the quickest way to spot an unquoted version number.
Indentation is the other YAML trap. Structure is defined entirely by spaces, tabs are forbidden, and one space too few moves a key to a different parent without any error. The YAML validator reports inconsistent indentation with a line number.
TOML: explicit and strict
TOML was designed to avoid YAML’s ambiguity. Strings are always quoted, and dates are a real type. Tables in square brackets create nested objects: [server] followed by port = 8080 is {"server": {"port": 8080}} in JSON. TOML to JSON converts the common core: tables, strings, numbers, booleans, dates, and arrays of simple values. Inline tables and arrays of tables ([[bin]]) are rejected instead of approximated. Dates become strings, because JSON has no date type.
INI: the format without a standard
INI files look simple: [section] headers and key=value lines. But there is no specification. Parsers disagree on comments, quoting, duplicate keys, and whether yes is a boolean. INI to JSON therefore keeps every value as a string and flags duplicates instead of silently choosing one. Convert types in the program that reads the result, where the meaning of each setting is known.
XML: elements, attributes, and repetition
XML has two places to store data, attributes and child elements, and no arrays. Converting to JSON requires conventions. The converters here use a common one: attributes become keys prefixed with @, text next to attributes becomes #text, and repeated sibling elements become arrays. So <profile id="a1"><tag>math</tag><tag>code</tag></profile> becomes {"profile": {"@id": "a1", "tag": ["math", "code"]}}. One trap: a single <tag> becomes a plain string, not a one-element array, so code that reads the JSON should accept both.
XML parsers can also be attacked through DTDs and entities (XXE and “billion laughs”). The XML formatter and converters reject DOCTYPE and entity declarations before parsing, so pasting untrusted XML is safe.
A safe conversion routine
- Validate the source file in its own format first.
- Convert to JSON and read the types of the values that matter.
- Quote strings that look like numbers, booleans, or dates, such as version numbers, postal codes, and country codes.
- Convert to the target format and diff the result against a known-good file, if you have one.
- Run the consuming tool’s own check, such as
docker compose config,kubectl apply --dry-run, or your application’s config loader.
Everything is parsed locally, which matters because configuration files routinely contain database URLs, internal hostnames, and API keys.
