Skip to main content

YAML to JSON

YAML to JSON converts a YAML document to JSON in your browser using the YAML 1.2 core schema. That matters more than it sounds: under the older 1.2 predecessor still used by PyYAML and Ruby, the unquoted tokens NO, yes, on and off resolve as booleans, so a list of ISO country codes loses Norway. Here they stay strings, and anything that would resolve differently under 1.1 is flagged.

Local-only
Indent
YAML
0 B1 lineLn 1, Col 1
JSONread-only
0 B1 line

How does YAML to JSON work?

YAML looks simple and is not. The specification is longer than JSON's, XML's and TOML's combined, and most of the surprises come from one design decision: unquoted scalars are resolved by pattern rather than declared by syntax. What a token means depends on which resolution table the reader is using.

The Norway problem is a versioning problem

YAML 1.1 resolved y, yes, on, n, no and off as booleans. So countries: [NO, SE, DK] loaded as [false, "SE", "DK"], and a configuration file that said norway: no meant something no one intended. YAML 1.2 removed all of them, along with sexagesimal numbers — which is why 1.1 read 12:30 as the integer 750.

The catch is that the software people actually run is split. PyYAML's default loader and Ruby's Psych are 1.1; Go's yaml.v3 and modern JavaScript parsers are 1.2. This converter applies 1.2 and tells you, by name, every token where a 1.1 reader would disagree — because the useful output is not just the JSON but the knowledge that the file is ambiguous.

Anchors are expanded, with a budget

&name defines an anchor and *name references it. JSON has no equivalent, so each reference is expanded into a copy. That expansion is the "billion laughs" attack: ten anchors each referencing the previous one twice expand to 1,024 nodes, twenty to a million, thirty to more than a billion. A 200-byte file can exhaust the heap. Expansion is capped at 100,000 nodes and the cap is reported, which is the difference between a clear error and a hung tab.

Block scalars keep their line breaks

| keeps newlines, > folds them into spaces, and the chomping indicator decides what happens to the trailing one: | keeps exactly one, |- strips them all, |+ keeps every one. That last distinction matters for a PEM key or a shell script whose final line has to be terminated. A # inside a block scalar is content, not a comment — a parser that strips it eats the shebang off every script.

Duplicate keys are reported, not hidden

YAML says a duplicate key is an error. Most parsers silently keep the last one. This one keeps the last, matching JSON.parse, and says which key and on which line — because a duplicate in a config file is almost always a merge gone wrong.

YAML

regions: [NO, SE]
version: "007"
retry: 012

JSON

{
  "regions": ["NO", "SE"],
  "version": "007",
  "retry": 12
}
// warning: 012 — leading zero, 1.1 reads it as octal

What options and edge cases does YAML to JSON support?

Resolution and support
ParameterTypeDefaultBehaviour & edge cases
true / falsebooleanresolvedOnly these two spellings, in any case. This is the whole difference from YAML 1.1 and the reason Norway survives.
yes / no / on / off / y / nstringflaggedStrings under 1.2, booleans under 1.1. Each one is reported by name so you know the file means different things in different runtimes.
~ / null / (empty)nullresolvedAll three spell JSON null. An empty value after a key is null, not an empty string.
012integer12, flagged1.2 reads a leading zero as decimal; 1.1 reads it as octal 10; the author probably meant the string. Quote it to keep the zero.
0o17 / 0x1finteger15 / 31Explicit octal and hexadecimal. 1.2 requires the 0o prefix for octal, which is why bare 017 is not 15.
.inf / .nanstringkept as textJSON has neither, so emitting one produces a document JSON.parse rejects. The token is kept verbatim and flagged.
Anchors and aliasesexpanded100,000 node capEach *alias becomes a copy, since JSON has no references. The cap stops a billion-laughs expansion from exhausting memory.
Merge keys (<<)not supportedreportedRejected rather than half-implemented. Merge semantics interact with overriding and ordering in ways that are easy to get subtly wrong and hard to notice.
Multi-document--- separatedarraySeveral documents become a JSON array. Turn the option off to convert only the first, which is what a Kubernetes manifest bundle usually wants.
Input sizebytes8 MBParsing runs on the main thread. Above the ceiling the tool declines rather than freezing the tab.

Frequently asked questions

Why is my `no` a string instead of false?

Because this parser applies the YAML 1.2 core schema, where only true and false are booleans. YAML 1.1 — still what PyYAML's default loader and Ruby's Psych use — resolves no, yes, on, off, y and n as booleans, which is how a list of ISO country codes silently loses Norway. If you want a boolean, write true or false; if you want the string, you already have it.

Which YAML version should I assume my tools use?

Check rather than assume, because the split is real and unversioned in practice: PyYAML's safe_load and Ruby's Psych are 1.1, Go's gopkg.in/yaml.v3 and most current JavaScript parsers are 1.2. The reliable answer is to quote anything ambiguous — a quoted scalar is a string in every version of YAML ever published, which makes the question moot.

What happened to my anchors?

They were expanded. JSON has no way to express a reference, so each *alias becomes a full copy of what the anchor held. This is lossless for the data and lossy for the structure: the fact that two parts of the document were the same object is gone. If you need that preserved, JSON is the wrong target.

Why did it refuse to expand my aliases?

Nested aliases multiply. If each anchor references the previous one twice, thirty levels expand to more than a billion nodes from a file you can read in one screen — the billion-laughs attack, which has taken down real services. Expansion stops at 100,000 nodes and says so, rather than growing until the tab dies.

Why does my number keep its quotes in the output?

Because you quoted it in the YAML, and a quoted scalar is a string in every version of YAML. That is the correct behaviour and usually the intent: version: "007" and version: 007 are genuinely different values, and only one of them survives a round trip as written.

Is my YAML uploaded?

No. The parse and the conversion both run in the tab you already have open — watch the Network panel while you paste. This matters specifically for YAML, because the YAML people need converted is usually a Kubernetes manifest, a CI workflow or a docker-compose file, which is to say a file full of infrastructure details and sometimes secrets.