Converting Swagger and OpenAPI specs between YAML and JSON
The single most common reason to convert YAML to JSON (or back) is an API spec. OpenAPI files are usually written in YAML because it's easier to hand-edit, but plenty of tooling — code generators, older validators, some gateway imports — wants JSON. Going the other way, teams that store openapi.json often want a YAML copy for review, because YAML diffs are far more readable in pull requests.
Two things matter when converting a spec, and most converters get at least one wrong:
- Key order must survive. An OpenAPI file reads top-to-bottom as documentation:
openapi, theninfo, thenservers, thenpaths. A converter that alphabetizes keys (or lets its language runtime reorder them) produces a technically-equivalent file that's much harder to review. This converter uses an order-preserving parser in both directions, sopathsstays where you put it — and numeric-looking keys like the'200'and'404'response codes keep their original order too, instead of being silently re-sorted the way plain JavaScript objects would. - Response codes are strings. In OpenAPI YAML, response codes are written quoted —
'200':— because unquoted200:would parse as the integer 200, and the OpenAPI schema requires string keys. When converting JSON → YAML, this tool keeps number-like keys quoted so the round trip stays valid.
Version fields are the other classic casualty: version: 1.0 in YAML is the number 1.0, which becomes 1 in JSON output (trailing zero gone). If your version is meant to be a string — and in OpenAPI's info.version it is — quote it in the YAML: version: '1.0'. The converter can't guess intent, but converting back to YAML it will quote any string that looks like a number, so a correct file stays correct.
What YAML features this converter supports — honestly
Full YAML is a big specification, and this page implements the common subset that covers config files, CI pipelines, Kubernetes manifests, and API specs — parsed by code written into this page, with no external parser library loaded. Supported: block mappings and sequences, nested indentation, flow style ({a: 1}, [1, 2]), single- and double-quoted strings with escapes, literal | and folded > block scalars with strip/keep chomping, comments, a single leading ---, and YAML 1.2 core-schema typing (null, booleans, integers including hex/octal, floats).
Not supported — and the converter will tell you so with the line number instead of silently mangling your data:
- Anchors and aliases (
&name/*name) — inline the repeated value before converting. JSON has no reference syntax, so any converter must expand these; this one asks you to do it explicitly rather than guessing. - Custom and explicit tags (
!Ref,!!timestamp) — common in CloudFormation templates. These carry semantics JSON can't express. - Merge keys (
<<:) — these depend on anchors. - Multiple documents in one file (a second
---) — convert one document at a time. For multi-document Kubernetes files, split on the---lines first. - Complex keys (
? key) and blank-line paragraph breaks inside folded scalars.
The design rule is: never silently drop data. Anything outside the subset produces a visible error pointing at the line, not a lossy approximation.
Doing the same conversion in Python
If you need this in a script rather than a browser, Python's PyYAML is the standard route. YAML to JSON:
import yaml, json
with open("spec.yaml") as f:
data = yaml.safe_load(f)
with open("spec.json", "w") as f:
json.dump(data, f, indent=2)JSON to YAML — note sort_keys=False, without which PyYAML alphabetizes your keys, and default_flow_style=False for block-style output:
import yaml, json
with open("spec.json") as f:
data = json.load(f)
with open("spec.yaml", "w") as f:
yaml.dump(data, f, sort_keys=False, default_flow_style=False,
allow_unicode=True)Always use yaml.safe_load, never yaml.load — full load can construct arbitrary Python objects from tagged YAML, which is a code-execution risk on untrusted input. Also worth knowing: PyYAML implements YAML 1.1, so it will read unquoted no as False (see the next section), while ruamel.yaml in its default round-trip mode follows 1.2 and preserves key order and comments.
One-liner from a shell, if you have Python installed:
python -c "import yaml,json,sys; json.dump(yaml.safe_load(sys.stdin), sys.stdout, indent=2)" < spec.yaml > spec.jsonCommon YAML pitfalls that break conversions
The Norway problem: when "no" becomes false
This is the most famous YAML bug class, and almost nobody explains it before you hit it. YAML 1.1 — the version implemented by PyYAML, older libyaml bindings, and plenty of tools still in production — treats all of these unquoted scalars as booleans: y, yes, on, true, n, no, off, false, in any capitalization.
So this innocent country list:
countries:
- SE # Sweden
- FI # Finland
- NO # Norwayparses under YAML 1.1 as ["SE", "FI", false]. Norway becomes the boolean false. The same trap hits on/off in feature-flag configs and the country code for Oman's neighbor... anywhere a real-world string collides with the 1.1 boolean list.
YAML 1.2 (2009) fixed this — only true/false are booleans — and this converter parses per 1.2, so NO stays the string "NO". But because you can't control which parser reads your file next, the converter does two extra things: it warns you when your input contains an unquoted 1.1-style boolean, and when emitting YAML it always quotes strings like no, on, and yes so the output is safe in any parser, 1.1 or 1.2. The fix in your own files is the same: quote it. - 'NO' is Norway in every YAML version ever released.
Tabs are not allowed for indentation
YAML forbids tab characters in indentation — only spaces. Editors configured for tab-indented files produce YAML that fails with confusing errors several lines away from the actual tab. This converter reports the exact line containing the tab. If your file mixes both, convert tabs to spaces first (most editors have a "convert indentation" command).
Unquoted strings that turn into other types
Beyond the booleans: version: 1.10 becomes the number 1.1 (trailing zero lost), zip: 01234 may be read as octal by 1.1 parsers, time: 12:30 is a sexagesimal number in YAML 1.1 (yes, base-60 — it parses as 750), and value: ~ is null, not a tilde. The rule that prevents every one of these: if it's meant to be a string, quote it. The converter warns on leading-zero scalars and 1.1 booleans, and its YAML output quotes anything ambiguous.
Indentation that's inconsistent between siblings
Sibling keys must start at exactly the same column. A key indented three spaces under siblings at two is an error — and in deeply nested OpenAPI paths blocks it's the most common hand-editing mistake. The error message here tells you the expected and found indent widths.
Related tools
Working with the JSON side of a conversion? These pair well: