JSON to YAML Converter_

Convert JSON to YAML and keep the values meaning what they meant. Nothing is lost going this way — but YAML reads bare words as booleans, dates and numbers, so a string like no can arrive at Kubernetes as false. This page quotes the values that would change, and tells you which ones they were.

Free, no account, and the file never leaves the page.

toolkit.codes/json-to-yaml
YAML
Nothing to convert yet

How to use

  1. 01Paste the JSON into the left pane, or pick a .json file off disk with Upload. The file is read in the tab.
  2. 02Leave Quoting on Defensive unless you know your consumer reads YAML 1.2. This is the setting that decides whether "no" stays a string.
  3. 03Read the panel beside the YAML. It names every value that was quoted for you and what it would have meant unquoted — that is the part other converters do not show.
  4. 04Set the indent and key order to match the file you are pasting into. Two spaces is the Kubernetes convention; leave key order alone unless you specifically want a sorted diff.
  5. 05Take the YAML with Copy, or save it as converted.yaml. Then read the indentation once before you commit — in YAML the whitespace is the structure.

A Kubernetes manifest generated as JSON

A controller emits JSON. Applying it works, but nobody can review it in a pull request, and the country codes in the node selector are about to become booleans. The quotes are the whole point: without them the cluster reads region: false and schedules nothing.

JSON
{"nodeSelector": {"region": "no"}, "replicas": 3}
YAML
nodeSelector:
  region: "no"
replicas: 3

A swagger.json you now have to edit

The spec is generated, and the next change is a long description that would need escaping in JSON. The version keeps its quotes — unquoted 1.10 is the number 1.1, which is a different release.

JSON
{"openapi": "3.0.0", "info": {"version": "1.10"}}
YAML
openapi: 3.0.0
info:
  version: "1.10"

A Docker Compose fragment

A tool produced JSON for a service block and it has to go into a compose file read with YAML 1.1 rules. Compose defines restart: no as a real policy name, so unquoted it becomes a boolean and the policy silently changes.

JSON
{"restart": "no", "ports": ["80:80"]}
YAML
restart: "no"
ports:
  - "80:80"

A CI config with a schedule in it

A pipeline definition holds start times as strings and is about to be read by a 1.1 parser. Unquoted, 12:30 is sexagesimal under YAML 1.1 and arrives as the integer 750.

JSON
{"window": {"start": "12:30", "end": "18:00"}}
YAML
window:
  start: "12:30"
  end: "18:00"

What each JSON value becomes, and what it would have become

JSON valueYAML here (Defensive)Unquoted, read as YAML 1.1
"no", "NO", "n""no" — quoted by this pagefalse — the Norway problem
"yes", "on", "y"quoted by this pagetrue
"off""off" — quoted by this pagefalse
"12:30""12:30" — quoted by this page750 — sexagesimal
"1.10""1.10" — already quoted by the emitterthe float 1.1, losing the trailing zero
"0755""0755" — already quotedthe octal integer 493
"null", "~"already quotednull
"true", "42", "1e5"already quoteda boolean or a number
"""" — already quotednull, which is not the empty string
"a: b"quoted — a bare colon would split the linea parse error or a nested mapping
"text #note"quoted — # starts a commentthe value text, silently truncated
"-lead"left bare — a dash with no space after it is not a sequencethe string, unchanged
" padded "quoted — bare scalars are trimmedthe string without its spaces
"a b" (a tab)left bare — legal inside a plain scalarthe string, unchanged; still worth quoting by hand for other parsers
a string with a newlinea |- block scalarthe same block scalar
{} / []{} / [] — the flow formthe same; YAML has no block way to write "empty"
a number past 2^53 (9,007,199,254,740,991)already rounded by JSON.parsethe rounded value — YAML never saw the original

Every row measured against the shipped emitter, not assumed. The third column is why the second column looks the way it does — it is what you get from a converter with no quoting policy.

The five controls on one document

SettingWhat it producesWhen you want it
Quoting: Defensiveregion: "no"The default. Anything going to Kubernetes, Ansible, Compose or GitLab CI
Quoting: Minimalregion: noA consumer you know reads YAML 1.2. Cleaner, and reported rather than silent
Style: Blockports: then - 80 on its own lineThe default, and what a manifest looks like
Style: Flowports: [ 80, 443 ]Rarely. It reproduces the JSON you started from
Indent: 2 / 4two or four spaces per level2 for Kubernetes and Compose; 4 where the house style says so
Key order: Keepspec before region, as in the JSONThe default. Preserves the order a human chose
Key order: Alphabeticalregion before specA stable diff — at the cost of moving apiVersion and kind off the top
Line width: 80 / 120long strings wrap onto continuation linesMatching a formatter or a review tool
Line width: noneone line per value however longAnything that will be diffed, or greppped

All five applied to the same input: {"spec":{"replicas":3,"ports":[80,443]},"region":"no"}

Tips

  • If the JSON might be malformed, run it through the JSON formatter first. This page reports the line and column of a parse error, but the formatter is built to show you every problem at once.
  • Leave key order alone by default. A sorted manifest diffs beautifully and reads badly, because apiVersion and kind stop being the first things you see.
  • Set line width to "No wrapping" before you generate anything that will be reviewed as a diff. A wrapped string re-flows when its neighbours change, and the diff shows lines nobody edited.
  • Two spaces is not a preference in the Kubernetes and Compose world, it is the convention. Four-space YAML parses identically and looks foreign.
  • If you are converting to hand-edit and then converting back, remember that the return trip drops the comments you just added. The YAML becomes the source of truth the moment you write your first comment in it.
  • A generated file that you convert once and then maintain by hand should get its anchors added by hand. Conversion cannot invent them — JSON has no references to convert.

Gotchas

A bare string can be a boolean, a number or a date

This is the one that costs people afternoons. YAML infers a type from the shape of unquoted text, and YAML 1.1 infers more aggressively than 1.2 — the boolean words, sexagesimal times, octal from a leading zero. The value in the file looks exactly like what you wrote; only the parser knows it decided otherwise. Defensive quoting covers the two families where the versions disagree, and the report names each one. Anything you add to the file later is on you.

Indentation is structure, so a hand-edit can change the data

JSON delimits with braces, so whitespace is decoration and an editor can reflow it freely. YAML delimits with indentation. Move a line two spaces left after conversion and it stops being a child of the key above and becomes a sibling — still valid YAML, still parses, entirely different document. This is why the conversion is worth doing carefully once rather than repeatedly: every hand-edit afterwards is an opportunity to change the meaning silently.

Flow style is valid YAML that undoes the conversion

Selecting flow everywhere produces {"a": [1, 2]} written as { a: [ 1, 2 ] } — the same document, the same shape, essentially the JSON you started with. If the reason for converting was readability or comments, flow gives you neither. It is offered because competitors offer it and because a short leaf list genuinely reads better inline, but as a whole-document setting it is almost never the answer.

Neither spec guarantees key order, even though every tool preserves it

A JSON object is an unordered set of pairs by the letter of the standard, and a YAML mapping is too. In practice every parser worth using preserves insertion order, and this page preserves it by default. The trap is depending on that in a system that round-trips the document through something you do not control — a templating layer, an API that reserialises. If order matters to correctness rather than to reading, it belongs in an array, which is ordered by both specifications.

Long strings wrap, and the wrap is not part of the string

At the default eighty columns a long value is broken across lines at a space. Reading it back folds those breaks into single spaces, so the value is unchanged and the round-trip law still holds — the report says when this happened. What changes is the file: a wrapped string re-flows whenever anything near it changes, so a one-word edit shows up as a five-line diff. Set the line width to "No wrapping" for anything that lives in version control. Strings containing an actual newline are different: those become |- block scalars and the line breaks in them are real.

Technical details

Input
Any valid JSON — object, array, string, number, boolean or null at the root. A bare scalar converts to a single-scalar YAML document.
Output
A YAML 1.2 document, emitted without a --- start marker. Written to be safe under YAML 1.1 readers as well, which is what the quoting control is for.
Engine
The yaml package (ISC), lockfile-pinned and loaded lazily on this page only. Serialisation goes through a document tree, so the quoting decisions are made by the emitter rather than by patching text.
Reported
Defensive quotes with the value each would have taken, exposed values under Minimal, key sorting, line wrapping, block scalars, flow style, a non-object root, an empty root, and integer precision already lost by JSON.parse.
Round trip
Asserted as a law in the test suite: for any JSON, parsing the emitted YAML with an independent parser returns the original data — under every combination of the five controls.
File handling
Uploads are read inside the page with the browser File API and are never transmitted; Download writes out what is already in the tab.
Limits
Input is refused above 1 MB, checked before any parsing — a tenth of what the JSON tools allow, because a YAML document tree carries layout for every node and costs far more to build than plain text. The output pane stops drawing past 300,000 characters. Copy and Download are unaffected and always carry the complete result.
Network
None from tool code. A test sweep calls every function this page uses with fetch and XMLHttpRequest replaced by stubs that throw, so a stray request fails the build instead of shipping. Disconnect from the network and the page still works.

Frequently asked questions

Does converting JSON to YAML lose anything?

No. Every JSON value has an exact YAML form, and the test suite asserts it as a law: convert, parse the YAML with an independent parser, and the data matches the input. That is the opposite of the return trip, which cannot preserve comments or references because JSON has nowhere to put them. What conversion can change is how a later reader interprets a bare value, which is what the quoting control exists for.

Why are some of my strings quoted and others not?

Because only some of them would be misread. The emitter already quotes anything ambiguous under YAML 1.2, and this page adds quotes to the two families where YAML 1.1 disagrees with 1.2 — the boolean words like no and off, and times like 12:30 that 1.1 reads as sexagesimal numbers. Everything else stays bare so the file is still readable. The panel beside the output lists every quote that was added and what the value would have meant without it.

What is the Norway problem?

YAML 1.1 treats twenty-two spellings as booleans, including no. The country code for Norway is NO, so an unquoted list of country codes contains one entry that parses as false. It is the standard example of YAML inferring a type from the shape of unquoted text. YAML 1.2 removed those aliases in 2009, but Kubernetes, Ansible and Go tooling still read 1.1, so the bug is very much alive.

How do I convert JSON to YAML in Python?

yaml.safe_dump(json.loads(text)) is the core of it. Watch three defaults: PyYAML sorts keys unless you pass sort_keys=False, it wraps at eighty columns unless you pass width=float("inf"), and it emits YAML 1.1 — which means it quotes the Norway family for you, the reverse of what the JavaScript emitters do. Use ruamel.yaml instead if you need comments preserved across a later edit.

Can I do this with yq?

Yes — yq -P . reads JSON and writes YAML, and yq -P -o=yaml is the explicit spelling. The same binary converts back with -o=json. yq is the right answer for a file too large for a browser tab, which on this page means anything over 1 MB.

Should I use block or flow style?

Block, essentially always. Flow style writes collections inline with braces and brackets, which produces something very close to the JSON you are converting away from — so it gives up the readability that was the reason to convert. Flow is genuinely nicer for a short leaf list, but this setting applies to every collection in the document rather than picking the short ones.

Is my JSON uploaded anywhere?

No — and it is worth asking, because the JSON people convert to YAML is mostly manifests and pipeline definitions with credentials in them. Two of the most visible converters for this conversion process server-side, and in November 2025 researchers found that a pair of them had been storing and publicly exposing submitted documents. The work is JavaScript running in this tab. Every function it calls is covered by a test that stubs fetch and XMLHttpRequest to throw, so a request that slipped in would break the build rather than reach a server — and you can confirm it for yourself by disconnecting and carrying on.

Why did my API version 1.10 keep its quotes?

Because unquoted it is a number, and the number 1.10 is 1.1 — the trailing zero is not something a float can carry. Version strings are the most common real-world case of a value that looks numeric and is not, which is why the emitter quotes them without being asked. The same applies to anything with a leading zero, like a permission mask of 0755 or a zip code.

UTF-8
Ready
100% LOCAL

JSON to YAML conversion is exact — which is not the same as safe

Every JSON document is already a valid YAML document, and every JSON value has an exact YAML representation. There is no data to drop, no construct without an equivalent, no decision the converter has to make on your behalf about what to throw away. The tests on this page assert that as a law: take any JSON, convert it, parse the YAML back with an independent parser, and you get the same data you started with. That law holds under every combination of the five controls above.

So the risk here is not loss. It is that YAML understands more kinds of bare text than JSON does, and it applies that understanding to the output of the conversion. In JSON, "no" is a two-letter string and there is nothing else it could be, because JSON has exactly one way to write a string and quotes are part of it. YAML lets you write a string without quotes, which means YAML has to guess, and the guess depends on which version of the specification is doing the reading.

The Norway problem, still live in 2026

YAML 1.1 recognises twenty-two spellings of boolean, including y, n, yes, no, on and off in several capitalisations. The country code for Norway is NO. A list of country codes written as bare words therefore contains one entry that is not a string, and the name stuck because it is so obviously a code and so quietly a boolean.

YAML 1.2 fixed this in 2009 by dropping the aliases: under the 1.2 core schema, no is the string it looks like. That would be the end of the story except that the tools people convert JSON for did not follow. Kubernetes, Ansible, Docker Compose, GitLab CI and everything built on Go's yaml.v2 read 1.1 rules. The parser on this page is 1.2. Emit region: no and this page agrees it is a string, right up until the manifest reaches a cluster that reads it as false.

The same split covers times. 12:30 is a string under 1.2 and a sexagesimal integer under 1.1 — sixty times twelve plus thirty, so 750. A shift start time in a config file becomes a three-digit number, and nothing in the file looks wrong.

What this page quotes, and what it deliberately leaves alone

The default is to quote exactly the strings whose meaning differs between the two versions — the boolean words and the sexagesimal pattern — and to leave every other value bare. That is a narrower rule than it might seem, because the underlying emitter already quotes anything ambiguous under 1.2 on its own: "1.10", "0755", "null", "true", "42" and the empty string all arrive quoted without any help. The gap is only where 1.1 and 1.2 disagree, and it is two families wide.

Quoting everything was the obvious alternative and it is the wrong one. A document where every value carries quotes is harder to read than the JSON it came from, which removes the reason to convert in the first place; and it teaches the reader nothing, because when every value looks dangerous none of them do. Quoting nothing is what most converters do, including ones that advertise themselves for Kubernetes configs. The middle is the useful place to stand, and the panel beside the output names every quote it added and what the value would have become without it.

The Minimal setting turns the extra quoting off for anyone whose consumer is genuinely 1.2 — a modern Rust or Python parser, or the YAML formatter on this site. It does not go quiet: it reports the values that are now exposed, so choosing the cleaner output is a decision rather than an accident.

Block or flow, and why flow defeats the point

YAML has two ways to write a collection. Block style puts one item per line and uses indentation for structure — the shape of every manifest you have read. Flow style writes { a: 1, b: 2 } and [1, 2, 3] inline, and it exists mostly because JSON is a subset of YAML and the grammar had to accommodate it.

Flow output is valid, and it is a strange thing to ask for. If you convert JSON to YAML and select flow everywhere, what comes out is very nearly the JSON you put in, with the quotes around the keys removed. The one place it earns its keep is a short leaf list — ports: [80, 443] reads better on one line than three — but this page applies the setting to every collection in the document rather than guessing which ones are short, so the honest description is that it is a whole-document switch and block is what you want.

The OpenAPI and Swagger case

The most common single reason to run this conversion is a generated swagger.json or openapi.json that somebody now has to edit by hand. Specifications are emitted as JSON by most toolchains and maintained as YAML by most humans, because YAML lets you write a description across several lines without escaping and lets you leave a comment explaining why an endpoint is deprecated.

Two things are worth knowing before you commit the result. Converting an OpenAPI document to YAML does not create the anchors that a hand-written spec would use to share schema fragments — the JSON had no references, so neither does the YAML, and the repetition you see was already there. And a spec full of version-shaped strings is exactly the document where quoting matters: an API version of 1.10 that loses its quotes becomes the number 1.1, which is a different version.

The Python and yq routes, and what they default to

For JSON to YAML in Python the short form is yaml.safe_dump(json.loads(text)). Three defaults will surprise you. PyYAML sorts keys alphabetically unless you pass sort_keys=False, which reorders a manifest you wanted left alone. It wraps lines past eighty columns unless you pass width=float('inf'). And it emits YAML 1.1, so it quotes the Norway family for you — the opposite trade from the JavaScript ecosystem, where the emitter is 1.2 and does not. If you need comments preserved on a later edit, ruamel.yaml is the library that keeps them and also preserves insertion order by default.

At the command line, yq -P . reads JSON and prints YAML, and yq -P -o=yaml is the explicit form; the same binary goes the other way with -o=json. For a one-off with no extra tooling installed, python3 -c 'import sys,json,yaml; yaml.safe_dump(json.load(sys.stdin), sys.stdout)' is available almost everywhere. All of them run locally, which matters more here than it sounds: the JSON people convert to YAML is mostly manifests and pipeline definitions, and those carry secrets.