YAML to JSON (and back)

Paste a configuration and pick the direction. Errors come with the right line, and the page tells you which values a YAML 1.1 reader would read another way: «no» turning into false, «22:22» turning into 1342.

The text and the files stay in your browser: the conversion happens on your device and nothing is sent to anyone.

Why «no» turns into false: the Norway problem

YAML exists in two versions that are both in use. 1.1 is from 2005 and it is the one of PyYAML, so of Python and Ansible; 1.2 is from 2009 and it is the one of the newer readers, including the one on this page. The text of the file is the same, but some words written without quotes are different things for the two versions.

The example that gave the problem its name is the code for Norway: in a list of countries NO is the boolean false for a 1.1 reader, and Norway disappears. It is not the only case. yes, on and off are booleans too, and indeed the on: key of GitHub Actions workflows, read with PyYAML, becomes true. 0755 is an octal number worth 493. 22:22, a port mapping in a docker-compose, is a base-60 number worth 1342. In YAML 1.2 they are all text, except 0755, which becomes the number 755.

That is why the page does not just convert: for every value written without quotes it asks both readers what they see, and if they disagree it tells you, with the line, the value here and the one of the other version. With the menu Read the YAML as you pick which reader to imitate. The 1.1 reading is PyYAML's, character by character; the 1.1 specification also lists y and n among the booleans, but PyYAML does not and here PyYAML is followed, because it is the 1.1 reader people actually have at hand. The cure, in every case, is the same: quotes.

Errors, on the right line

It is the reason nearly everybody opens a converter: the program says the file is wrong, and does not say where. Here every error has a name, the line and an excerpt with the line highlighted, and the button Go to line selects it in the box.

The most common cases have an explanation of their own. A tab in the indentation, which YAML does not accept and which looks just like spaces (in the excerpt it shows as an arrow); and, in the 1.1 reading, also one after the colon or at the end of the line, which YAML 1.2 lets through and PyYAML does not. An indentation not aligned with the lines on the same level. A value with a colon followed by a space, like title: Rome: a guide, which YAML takes for a new key. An apostrophe inside single quotes, as in 'Don't panic', which closes the text halfway. A value without quotes that starts with an at sign, like @types/node, or with an Ansible variable, like {{ app_version }}. The missing space after the colon. A repeated key, which PyYAML would take without a word, keeping the last one.

The nastiest case is the quote left open: the reader only notices it lines later, and points at the wrong line. Here the page goes looking for the line where the quote or the bracket opens without closing, and highlights that one. The reader's original message stays underneath anyway, for whoever wants it.

What JSON cannot say, said

The dates stay text, exactly as written, because JSON has no type for dates; the page lists the lines, because PyYAML would turn them into dates instead. The whole numbers above 2^53, like a twenty-digit order code, are written in the JSON digit by digit, but the page warns that a JavaScript program reading them back rounds them: 12345678901234567890 would become 12345678901234567000. The values infinity and «not a number» become null, and they are listed. Numbers that change shape are flagged separately: 3.10 becomes 3.1, which is the same number but not the same Python version.

The tags like !Ref and !Sub of CloudFormation or !reference of GitLab do not exist in JSON: the value stays, the tag does not, and the page lists which ones and where. Several documents split by --- become a list. The anchors and aliases are expanded, and there is a declared limit here: if the expansion adds more than 100,000 values, the conversion stops. It is the defence against the attack called «billion laughs», ten lines that expanded become a billion values and freeze any program reading them to the end.

JSON to YAML: the quotes that are really needed

In the other direction the risk is reversed. A text like "no", "010" or "12:30" is plainly a text in JSON; written in YAML without quotes, the first 1.1 reader turns it into a boolean or a number. Here every text that even one of the two readers would read as something else goes in quotes, and the page tells you which ones.

Pick the indentation, two or four spaces, or the compact form on one single line. If the JSON is a list, you can turn it into several YAML documents split by ---, the way it is done with Kubernetes files. And if the JSON cannot be read, the error has a name here too: the trailing comma before the bracket, single quotes, the comment, the key without quotes. The JSON reader is written on purpose instead of using the browser's one, which rounds big whole numbers and points at the error differently in every browser. To tidy up a JSON without changing format, indent it or sort its keys, there is JSON formatter & validator.

How it was tested

The judge is not this page: it is PyYAML, the YAML reader of Python, used in two ways. As it is, it gives the 1.1 reading; with the Core Schema rules rewritten from the text of the 1.2 specification, it gives the 1.2 reading. On 500 randomly generated documents, full of the traps above, with anchors, merges and several documents, the values of the page must match its own in both readings, and the list of flagged lines must be exactly the list of lines on which the two PyYAML readings disagree, not one more and not one less. The same goes for tabs: on 600 documents with a tab put in at random, the page's 1.1 reading accepts and rejects exactly the ones PyYAML accepts and rejects, and when it stops for the tab it points at the same line and the same column.

In the other direction, 400 randomly generated JSON structures, written with three different indentations, go all the way round: from JSON to YAML and back to JSON, and at the end they must come back identical. In the middle the YAML produced is read back by PyYAML in both versions, and it must give the same data as the starting JSON. Finally the JSON reader is compared with the json module of Python on 1,200 texts spoiled on purpose: they must accept and reject the same ones, and point at the same line.

What it does not do

It does not keep comments. JSON has none, so going from YAML to JSON they disappear, and going back they do not reappear. It does not check that the file is a valid configuration for the program that will read it: only docker knows whether a docker-compose has the right fields. It does not turn CloudFormation tags into the long form used in the JSON of that service, it removes them and says so. It does not guess every encoding: it reads UTF-8 and UTF-16, and a file saved in another one is read as Windows-1252, saying so.

It does not convert other formats. For XML there is XML to JSON and back, to turn a JSON list into a table there is JSON to CSV, and if the YAML file sits inside a ChatGPT or Claude answer, to pull it out clean there is Extract code from AI.