Converters

How to Convert JSON to YAML (Without Corrupting Your Values)

How to convert JSON to YAML: how objects and arrays become indented blocks, which values have to be quoted to survive, why long IDs and 1.50 get rounded by naive converters, and how multi-line text is written - with a worked example.

6 min readUpdated Aug 26, 2026

JSON is what machines emit: an API response, a package manifest, a config export. YAML is what people edit - Docker Compose files, Kubernetes manifests and CI pipelines are all YAML, because indentation reads better than nested braces. Converting between them is one of the most common chores in a developer's week, and quietly one of the easiest to get wrong, because YAML infers the type of every unquoted value from its shape. This guide explains how the JSON to YAML converter maps one format onto the other, which values it has to quote and why, and how it keeps your numbers exactly as you wrote them.

How JSON maps onto YAML

The two formats describe the same three building blocks, so the mapping itself is simple. A JSON object becomes a YAML mapping: each key: value pair on its own line, with no braces and no commas. A JSON array becomes a YAML sequence: one dash-prefixed item per line, with no brackets. A JSON scalar - string, number, true, false or null - becomes a YAML scalar, usually written without any quotes at all.

The one structural difference is how nesting is shown. JSON opens a brace or a bracket; YAML indents the child lines further to the right and lets the whitespace carry the meaning. Every level of nesting becomes one more step of indentation, and the punctuation disappears - which is the whole reason people convert.

Two cases have no indentation to give them away, so YAML borrows JSON's own syntax: an empty object is written {} and an empty array [].

A worked example

Take this service configuration as JSON - a few top-level settings, an array, a nested object and a two-line message:

  • {
  • "name": "web-server",
  • "version": "1.20",
  • "port": 8080,
  • "enabled": true,
  • "maintainer": null,
  • "region": "NO",
  • "tags": ["production", "critical"],
  • "database": { "host": "db.internal", "port": 5432 },
  • "motd": "Deploy window\nTuesdays 02:00 UTC"
  • }

Paste it into the JSON to YAML converter and you get this back:

  • name: web-server
  • version: '1.20'
  • port: 8080
  • enabled: true
  • maintainer: null
  • region: 'NO'
  • tags:
  • - production
  • - critical
  • database:
  • host: db.internal
  • port: 5432
  • motd: |-
  • Deploy window
  • Tuesdays 02:00 UTC

Most of it is the obvious translation: braces gone, quotes gone, the array turned into two dashed lines, the database object indented one level. But look at the three lines that are not obvious - version and region came back in quotes, and motd grew a | marker. Those are the parts worth understanding, because they are exactly what a careless conversion gets wrong.

Why some values have to stay quoted

In JSON, quotes are what make a string a string. In YAML most strings need no quotes, so the loader works out each value's type from its shape - and a string that looks like something else silently changes type on the way in. The best-known casualty is the country code for Norway. Under the YAML 1.1 rules that PyYAML and Ruby's Psych still use, the bare word NO is a boolean, so "region": "NO" written out unquoted comes back as region: false. This is the famous "Norway problem", and it has broken real deployments.

It is not just NO. The same rules catch a whole family of ordinary strings:

  • yes, no, on, off, y and n - all read as booleans, in any capitalisation.
  • A zero-padded number like 017 - read as octal, so it becomes 15.
  • 0x1F and 0b101 - read as hexadecimal and binary numbers.
  • 1_000 - the underscore is a digit separator in YAML 1.1, so this is one thousand.
  • A duration written 1:30 - read as sexagesimal, so it becomes 90.
  • 2026-08-25 - read as a date object, not the text you typed.
  • A version string like 1.20 - read as the number 1.2, losing the trailing zero.

The converter checks every string against the union of the YAML 1.1 and YAML 1.2 rules and quotes it if either one would re-type it. That is deliberately conservative: you rarely know which loader will read the file at the other end, and a quoted string that did not need quotes costs two characters, while an unquoted one that did can cost you an outage. It is why version: '1.20' and region: 'NO' came back quoted above.

Numbers keep their exact digits

A subtler problem sits on the numeric side, and most converters have it. The obvious way to build one is to hand your text to JSON.parse and re-serialise the result - but JSON.parse turns every number into a JavaScript double, and doubles cannot hold every JSON number. A 20-digit identifier such as 12345678901234567890 comes back as 12345678901234567000, and 1.50 comes back as 1.5. Those are exactly the values people notice going wrong: database IDs, order numbers, pinned versions.

This converter reads each number straight out of your JSON text and writes those same digits back untouched, so a long ID keeps every digit and 1.50 stays 1.50. There is one number it does change on purpose: an exponent like 1e5. That is a float under YAML 1.2, but under the YAML 1.1 rules it is a plain string, because that resolver requires both a decimal point in the mantissa and an explicit sign on the exponent. Writing it as 1.0e+5 satisfies both, and invents no digits that were not already there.

Multi-line text becomes a block scalar

A JSON string carries newlines as \n escapes, which becomes unreadable once a message runs past one line. YAML has a better answer: the literal block scalar, a | marker followed by the text indented beneath the key. That is what happened to motd above - the escape disappeared and the two lines are simply there.

The trailing dash in |- is the chomping indicator, and it controls the final newline: |- means the string ends without one, plain | means it ends with exactly one. A block scalar cannot express every string, though. If a line ends in a space, or the text starts with one, the indentation rules would quietly change the value - so in those cases the converter falls back to a double-quoted string instead. That is always exact, because YAML's double-quoted style is a superset of a JSON string and uses the same escapes.

Duplicate keys and other edge cases

JSON permits the same key twice in one object, and parsers resolve it by keeping the last value. YAML cannot write a duplicate key at all, so the converter follows the JSON rule - last value wins - and tells you how many duplicates it found, so the collision is not silent. Keys get the same treatment as values: a key such as yes, 1 or 2026-08-25 is quoted, because a re-typed key is as damaging as a re-typed value.

You can also choose a two-space or four-space indent. Two is the near-universal convention in Kubernetes and CI config and is the default; four is easier to follow in deeply nested files. Either way, list dashes are padded so nested items line up under them.

It runs entirely in your browser

Config files hold hostnames, connection strings and tokens, so where a conversion happens is not a detail. The JSON to YAML converter works entirely inside your browser tab - nothing is uploaded, and you can paste a production manifest without it leaving your machine. Going the other way, the YAML to JSON converter completes the round trip, and the JSON Formatter will validate and pretty-print your JSON first if you are unsure it parses.

Frequently asked questions

Why did my value come back wrapped in quotes?
Because unquoted YAML is typed by shape, and that string would have been read as something else. The country code NO and the words yes, no, on and off are booleans to a YAML 1.1 loader such as PyYAML or Ruby's Psych - the 'Norway problem'. Values like 017, 0x1F, 1_000, 1:30, 2026-08-25 and 1.20 are read as an octal number, a hex number, a thousand, ninety seconds, a date and the number 1.2. Quoting is the only way to keep them as the exact text your JSON had.
Will a long ID or a version like 1.50 be rounded?
No. Each number is read straight out of your JSON text and written back with the same digits, so 12345678901234567890 keeps all twenty and 1.50 stays 1.50. That is not automatic: a converter built on JSON.parse turns numbers into doubles first, which rounds that ID to 12345678901234567000 and rewrites 1.50 as 1.5. The single deliberate change is that an exponent such as 1e5 is written 1.0e+5, since a bare 1e5 is read as a string rather than a number under the YAML 1.1 rules.
Is my JSON uploaded anywhere?
No. The conversion runs entirely in your browser and nothing is sent to a server, so a config file containing hostnames, tokens or connection strings never leaves your device.