Convert YAML to JSON, and back
Paste a document and it comes back converted. Beside it: the anchors resolved, the duplicate keys with their line numbers, and every bare scalar whose type changes between YAML 1.1 and YAML 1.2.
How it works
A YAML scalar written without quotes carries no declared type. The reader assigns one by matching the text against a table of regular expressions given in the specification, and that table is not the same in 1.1 as it is in 1.2. Most of the surprises this format is known for come from that single difference. The reader on this page is the yaml package, version 2.9.0, which follows 1.2 unless a document says otherwise in a version directive of its own. Both tables are offered here, and every scalar the two read differently is listed under the output.
The Norway problem
Under 1.1 the boolean table takes in y, yes, on, n, no and off, each in lower case, upper case and title case. A list of country codes therefore loses Norway: the code no arrives as the boolean false. Version 1.2 keeps only true and false in that table, so no stays a string. Measured against the reader here, a value of no gives the string under 1.2 and the boolean under 1.1. Keys are caught by the same rule, and more quietly: under 1.1 the mapping written n: 1 has the key false.
Numbers that were never numbers
A bare scalar of digits separated by colons is a number under 1.1. The base-sixty integer type reads 22:22 as 1342 and 12:34:56 as 45296, both measured here, so a maintenance window written without quotes stops being a time. A host and port such as example.com:8080 is left alone, and so is 99:99, whose second group is out of range. A leading zero means octal under 1.1: the file mode 0755 comes back as 493, where 1.2 reads the same characters as the decimal 755 and spells octal with a 0o prefix instead. Underscores are digit separators under 1.1, so 1_000 is a thousand there and a string under 1.2.
Both versions agree that 1.10 is a number, and a number keeps no memory of how it was written, so a release identifier typed without quotes comes back as 1.1 and the trailing zero is gone for good. This one is worse than the others because it survives the conversion looking reasonable.
Five ways to write nothing
An empty value resolves to null under both versions, as do a tilde and the word null in any of its three casings. A key with nothing after its colon is a null rather than an empty string, which matters to any program that tests for one and not the other. Quotation is the only thing that holds text as text: a quoted no is the string no in every version of the specification, and quoting is the fix for every case above.
Indentation is the structure, and a tab is not indentation
Nesting is expressed by leading spaces, which makes whitespace load-bearing in a way JSON never allows, and the specification forbids a tab character in that position. The reader here reports it as TAB_AS_INDENT with a line and a column, then carries on and produces a value anyway. Measured: a two-line mapping whose child is indented with one tab came back with the parent key set to null and the child promoted to the top level. That error row is the only thing between a mistyped tab and a plausible wrong answer.
Anchors, aliases and merge keys
An anchor names a node and an alias repeats it, which is the feature JSON has no counterpart for. Converting expands every alias into a full copy, so the output grows. The merge key, written as two angle brackets, splices an aliased mapping into the one holding it and belongs to the 1.1 type set rather than the 1.2 core schema: the reader here merges under 1.1 and keeps the key as an ordinary property under 1.2, where it lands in the JSON literally. Expansion stops at 100 aliased nodes, which is what prevents a short document from expanding into a vast one.
Block scalars and the trailing newline
A pipe opens a literal block that keeps its line breaks; an angle bracket opens a folded block that turns single breaks into spaces. The character after the indicator decides the end of the value. Measured on a two-line block holding a and b, followed by two blank lines: the bare pipe gives one closing newline, a minus gives none, and a plus gives all three. The folded form gives a single line reading a b, again with one closing newline. That is one character of difference between a file that ends cleanly and one that does not.
JSON in both directions
Section 1.3 of the YAML 1.2 specification states that the format is a superset of JSON, and the grammar was revised in that version to make it so. YAML 1.1 promised nothing of the kind. Going the other way, YAML offers three things JSON has no room for: comments, anchors, and the choice between block and flow style. A comment that never existed cannot be recovered, and one added by hand is gone as soon as the document is converted back. Under 1.2 the writer here quotes any string an older reader would re-type, so the output survives both.
Every conversion is checked rather than asserted. The output is read back with the same reader, re-serialised, and compared with the same serialisation of the input character for character, which also proves that key order came through. The answer appears in a row beside the document, and it reports a mismatch as readily as a match.
Questions
Why did my value change from no to false?
Because the spec version is set to 1.1, where the boolean table includes no, off and n along with their upper-case and title-case forms. Switch to 1.2, where the table holds only true and false, or put quotes around the value, which pins it as a string under every version. The row listing version-sensitive scalars names each one with its line and column.
Which version does this use by default?
1.2 with the core schema, which is what the yaml package version 2.9.0 does with no version given. A document may override the control with a directive on its first line, and when it does, a row says so and names the version actually applied. That directive beats the control because a reader in the wild would obey it too.
Where did my comments go?
JSON has no comment syntax, so a converted document carries none and the count of what was dropped is reported instead. The reader keeps them in its own tree, but there is no property, no key and no value in JSON that a comment can become without changing the data. Converting back cannot return them.
What happens to anchors and aliases?
By default each alias is expanded into a full copy of the anchored value, because that is what makes the JSON loadable. Turn expansion off and each alias becomes a marker naming its anchor, which records where the sharing was and gives up being data. In the other direction, turning expansion off makes the writer emit identical objects and arrays of two or more entries once, with an anchor.
Can it convert a multi-document stream?
Yes. Documents separated by three hyphens are all read, and because JSON has no multi-document form the output is an array with one element per document in stream order. The count is reported so that a single-document result and a one-element array are never confused.