In 2019 a developer reported a bug that has been quietly breaking config files ever since. Their country list had an entry for Norway, country code NO. After a trip through YAML, Norway had vanished — and in its place was the boolean false.
This is the Norway problem, and it is not a parser bug. It is YAML working exactly as designed. YAML infers the type of an unquoted value from how the text looks, and in the widely used YAML 1.1 rules, no is a perfectly valid way to write "false" — along with yes, on, and off.
JSON never has this problem, because JSON puts quotes around every string. The moment you convert JSON to YAML, those quotes come off — and any value that looks like something else quietly becomes that something else. This guide covers exactly which values are at risk, how structure translates, and what a safe conversion looks like.
The Norway Problem
Here is the failure in full. This JSON is unambiguous — every value is a string:
{ "country": "NO", "shipping": "yes", "debug": "off" }Converted carelessly, the quotes disappear and a YAML 1.1 parser reads all three as booleans:
country: NO # parsed as false
shipping: yes # parsed as true
debug: off # parsed as falseNothing errors. Your file loads, your pipeline runs, and somewhere downstream a country code has become False. The fix is simply to keep the quotes, and a conversion that understands YAML does it for you:
country: "NO"
shipping: "yes"
debug: "off"true and false. But plenty of tooling still follows the 1.1 rules — PyYAML, which sits under Ansible and countless Python scripts, treats yes, no, on, and off as booleans by default. Quoting costs nothing and works under both versions.
Why YAML Guesses Types at All
The difference between the two formats comes down to one design choice.
JSON is explicit. A string is anything inside double quotes. "28" is a string, 28 is a number, and there is never a question about which is which.
YAML is implicit. Quotes are optional, so the parser has to infer the type from the shape of the text. 28 becomes a number, true becomes a boolean, null and ~ become null, and everything else falls back to being a string.
That inference is what makes YAML pleasant to write by hand — no punctuation for the common case. It is also exactly why converting into YAML needs care: the conversion has to notice which strings would be misread once their quotes are gone, and put them back.
Which Values Must Stay Quoted
Four categories of string need protection. CodBolt's converter adds the quotes automatically, but these are worth recognising in any YAML you review.
1. Boolean and null lookalikes
The classic case, matched case-insensitively so NO, No, and no are all caught:
{ "a": "NO", "b": "yes", "c": "Off", "d": "TRUE", "e": "null", "f": "~" }
a: "NO"
b: "yes"
c: "Off"
d: "TRUE"
e: "null"
f: "~"2. Numbers stored as strings
This one bites hardest on identifiers with leading zeros, which lose them silently:
{ "zip": "10001", "id": "007", "minor": "2.1", "version": "2.1.0" }
zip: "10001"
id: "007"
minor: "2.1"
version: 2.1.0Note the last line. 2.1.0 has two dots, so it is not a valid YAML number and stays a string on its own — no quotes needed. 2.1 would become the number 2.1, so it keeps them.
3. Strings with YAML punctuation
A colon starts a key, a hash starts a comment, and brackets and braces start inline collections. Any string containing them gets quoted so it stays a value:
{ "ports": "80:80", "note": "50% #1 choice" }
ports: "80:80"
note: "50% #1 choice"4. Empty strings and edge whitespace
An empty string with no quotes is indistinguishable from no value at all, and leading or trailing spaces would be trimmed away. Both keep their quotes:
{ "blank": "", "padded": " spaced " }
blank: ""
padded: " spaced "age: 28, a genuine boolean stays active: true, and null stays null. The output reads like handwritten YAML, not like JSON with the braces removed.
How Structure Translates
Once quoting is handled, the structural mapping is refreshingly direct — YAML and JSON describe the same shapes.
Objects become indented blocks. Each key sits on its own line, and nesting is expressed by indenting two more spaces:
{ "services": { "web": { "image": "nginx", "ports": ["80:80"] } } }
services:
web:
image: nginx
ports:
- "80:80"Arrays become sequences. Every item gets its own line beginning with a dash:
{ "skills": ["JavaScript", "Python"] }
skills:
- JavaScript
- PythonFor an array of objects, the first key shares the dash line and the rest line up underneath it. This is the idiomatic YAML layout and what tools like Docker Compose and GitHub Actions expect:
{ "books": [ { "title": "Dune", "year": 1965 },
{ "title": "Neuromancer", "year": 1984 } ] }
books:
- title: Dune
year: 1965
- title: Neuromancer
year: 1984Empty collections stay inline. There is nothing to indent, so an empty array is written [] and an empty object {} — both valid YAML, using its JSON-compatible flow style:
{ "tags": [], "meta": {}, "note": null, "count": 0 }
tags: []
meta: {}
note: null
count: 0Indentation Is the Syntax
In JSON, indentation is decoration — you can minify a file to one line and it still means the same thing. In YAML, indentation is the structure. Change it and you change the data.
Two rules follow from that, and both catch people out when they hand-edit converted files:
- Tabs are not allowed for indentation. YAML requires spaces. A stray tab is a parse error, not a style issue — which is why editors configured for tabs cause so much YAML grief.
- Sibling keys must align exactly. One extra space turns a sibling into a child, changing the shape of your data without any error message.
A converter sidesteps both problems by emitting consistent two-space indentation throughout. The risk starts when you edit the result by hand — so if a converted file suddenly misbehaves, check the indentation of the lines you touched first.
How to Convert JSON to YAML, Step by Step
- Open the JSON to YAML converter.
- Drop your JSON in on the left — paste it, or hit Upload for a file of up to 100 MB.
- Hit Convert. The YAML lands on the right, indented two spaces per level, with risky strings already wrapped in quotes.
- Read the quotes. Each one marks a value that would have changed type without them, which makes the output double as a list of the fields worth checking.
- Grab it with Copy, or hit Download for a
data.yamlfile ready to commit.
The Sample button loads a record with a list and a nested block in it, which is enough to show how a real config file will come out.
Nothing leaves your machine during any of this — worth knowing, since config files are exactly the kind of file that tends to hold connection strings and API keys.