Search tools

Find a tool by name or what it does.

Converters guides

JSON, YAML and TOML: which one should your config be?

One is for machines talking to machines, one is for humans editing files by hand, and one is the quiet compromise in the middle. Pick wrong and you inherit a very specific set of bugs.

6 min read

On this page

It is machines versus humans, not good versus bad

These three formats are not competing at the same job, which is why arguing about which is best goes nowhere. JSON was designed for data moving between programs. YAML was designed for a person to open a file and change a setting. TOML was designed for the very common case that sits between the two.

That single distinction predicts almost everything else about them. JSON is strict and punctuation heavy because a machine does not mind typing quotes and braces, and strictness means two programs never disagree about what a file says. YAML strips the punctuation out and lets you write comments, because a human editing a file at midnight needs both.

The cost of that friendliness is real, and it is the reason this article exists. YAML guesses what you meant. Most of the time it guesses right. The times it guesses wrong are legendary.

TL;DR

Data in transit between programs: JSON. A file a human opens and edits, especially a deeply nested one: YAML, with every ambiguous value quoted. Flat-ish settings with a handful of sections: TOML. If a file is both read by humans and generated by machines, author it in YAML or TOML and convert on the way out.

What JSON refuses to do

JSON has four types of value, an object, an array, and nothing else. That tiny surface is the whole point: there is nothing to disagree about, so a file written by one system means exactly the same thing to another. It is an excellent wire format and a mediocre config format, for three specific reasons.

It has no comments. None, by design. The usual workaround is a fake key, so you end up with things like a "_comment" entry sitting alongside real settings, which every reader has to learn to ignore and which some strict schemas will reject outright. Some tools accept a relaxed dialect with comments, but the moment your file is read by something else, they break.

It has no trailing commas. Delete the last item from a list and you have created a syntax error somewhere above where you were looking. This is a small thing that costs an enormous amount of collective time, and it is the most common reason a config file suddenly fails to load after a one-line edit. A JSON formatter will point at the exact character, which is usually faster than re-reading the file.

It has no date type. Every timestamp in a JSON file is a string, and the format of that string is a convention rather than a rule. One system writes an ISO timestamp, another writes a Unix number, a third writes something with a local timezone baked in, and nobody finds out until a booking lands on the wrong day.

The Norway problem, and why you should just use quotes

Here is the famous one. Write a list of country codes in YAML and include Norway, whose code is NO, and older parsers will read that unquoted NO as the boolean false. Your list of countries quietly contains a false where Norway should be.

The accurate version of this story matters. YAML 1.1 treated yes, no, on, off, y and n as booleans alongside true and false. YAML 1.2 narrowed that to true and false only. But a great many parsers in production today still behave the 1.1 way, or sit somewhere in between, so you cannot safely assume the version of the spec your file will meet.

The consequence is a habit rather than a rule: quote anything that could be misread. "no" is a string. no might not be. It costs two characters and removes an entire category of bug, and it is the single most valuable thing to know about the format.

This bites hardest in configuration where a two-letter value is normal: country codes, language codes, chemical symbols, feature flags written as on and off, and answers in a survey schema.

Version numbers, leading zeros, and the tab that is not allowed

The same guessing engine mangles numbers. Write a version as 1.10 and you get the number 1.1, because trailing zeros do not survive being read as a decimal. Version 1.10 and version 1.1 are very different releases, and the file will not warn you.

Leading zeros are worse. A zip code, a phone extension or an account number written as 0123 may be read as a number, losing the zero, and in some parsers a value beginning with 0 is interpreted as octal, so 0755 becomes 493. Quote them all.

Then there is whitespace. Indentation is structural in YAML, and tabs are illegal as indentation, full stop. An editor that helpfully converts your spaces to tabs produces a file that fails to parse with an error message that rarely says the word tab. Set your editor to spaces for YAML files and stop thinking about it.

Because structure is carried by indentation, a badly indented block is often still valid YAML, just describing something different from what you intended. That failure mode is much nastier than a syntax error, and it is worth running a suspect file through a YAML formatter or converting it with YAML to JSON to see the nesting laid out in braces.

Deep nesting undoes the whole argument

YAML is easier to read than JSON for one or two levels of nesting. At five or six levels it is harder, and this is the part people forget when they migrate a config file for readability. Braces are ugly but unambiguous: you can see where a block ends. Six levels of indentation in a long file gives you no such landmark, and reviewing a change means counting spaces in a diff.

If your config has grown that deep, the format is not the problem. Split the file, flatten the structure, or accept that a machine-oriented shape wants a machine-oriented format. Converting a monster with JSON to YAML makes it shorter, not clearer.

FormatComments allowed?Best forBiggest trap
JSONNoData sent between programs, APIs, stored recordsNo comments, no trailing commas, dates are just strings
YAMLYesConfig a human edits, deeply structured settingsUnquoted values silently change type, and tabs are illegal
TOMLYesFlat-ish settings with a few named sectionsNested arrays of tables get confusing fast

TOML, the boring middle

TOML looks like an old-fashioned settings file, with sections in square brackets and plain key equals value lines. It allows comments, it does not care about indentation, and crucially it does not guess: a quoted string stays a string, and there is no Norway problem because bare no is simply not a boolean.

It also has a real date type, with offsets and local variants built into the spec rather than left to convention. If your config carries timestamps, that alone is a strong argument.

Its weakness is depth. Deeply nested structures and arrays of tables become awkward quickly, with repeated bracket headers that are easy to get subtly wrong. TOML is excellent for a hundred settings in ten groups, and poor for a tree. Try it with JSON to TOML and you will see instantly whether your data suits it.

The rule

If a program writes it and a program reads it, use JSON. Strictness is a feature when nobody is typing.

If a human writes it and it is genuinely nested, use YAML, and quote every value that is not obviously a word: anything two letters long, anything that starts with a zero, anything that looks like a version, and every yes or no.

If a human writes it and it is mostly flat, use TOML. You get comments and dates without the guessing.

And when a file needs to be both, pick the human format as the source of truth and generate the machine format from it. That is a conversion, not a migration, and it takes seconds with YAML to JSON.

Tools from this guide

Keep reading