How to Convert YAML to JSON in JavaScript and Python (Safely)
Sooner or later every pipeline needs to turn YAML into JSON — feeding a Kubernetes manifest to an API, loading CI config in a Node script, or transforming Ansible variables in Python. The conversion itself is two lines of code. The interesting parts are the sharp edges: which loader is safe for untrusted input, what silently disappears in the round trip, and how to do it in one line from the shell. This guide covers all of it for JavaScript (js-yaml), Python (PyYAML), and the command line (yq).
JavaScript: js-yaml
The de facto standard in the Node ecosystem is js-yaml(npm install js-yaml). Parse with load, serialize with dump, and pair them with the built-in JSON object:
const fs = require('fs');
const yaml = require('js-yaml');
// YAML → JSON
const doc = yaml.load(fs.readFileSync('config.yaml', 'utf8'));
fs.writeFileSync('config.json', JSON.stringify(doc, null, 2));
// JSON → YAML
const data = JSON.parse(fs.readFileSync('config.json', 'utf8'));
fs.writeFileSync('config.yaml', yaml.dump(data, { indent: 2 }));
// Multi-document files (k8s manifests separated by ---)
const docs = yaml.loadAll(fs.readFileSync('all.yaml', 'utf8'));
// → array of parsed documentsUntrusted input: in modern js-yaml (v4+), load uses the safe schema by default and will not construct arbitrary objects — the old dangerous behaviour now requires explicitly opting into DEFAULT_FULL_SCHEMA via a legacy API. If you are stuck on js-yaml v3, use safeLoad, never load, for anything a user can upload. Wrap parsing in try/catch: YAMLException includes the line and column of the syntax error.
Python: PyYAML — safe_load or Nothing
Python's standard choice is PyYAML (pip install pyyaml). Here the loader choice is a genuine security decision, not a style preference:
import json
import yaml
# YAML → JSON
with open('config.yaml') as f:
data = yaml.safe_load(f) # ALWAYS safe_load
with open('config.json', 'w') as f:
json.dump(data, f, indent=2)
# JSON → YAML
with open('config.json') as f:
data = json.load(f)
with open('config.yaml', 'w') as f:
yaml.safe_dump(data, f, default_flow_style=False,
sort_keys=False) # keep original key order
# Multi-document input
docs = list(yaml.safe_load_all(open('all.yaml')))Why the insistence on safe_load? Full yaml.load honours language-specific tags like !!python/object/apply, which can instantiate arbitrary Python objects — including a call to os.system — the moment the file is parsed. A YAML file from a user upload, a webhook, or even another team must be treated as code under load:
# malicious.yaml — parsing this with yaml.load(f, yaml.Loader)
# runs the command:
exploit: !!python/object/apply:os.system ["rm -rf /tmp/x"]
yaml.safe_load(open('malicious.yaml'))
# → yaml.constructor.ConstructorError (blocked — safe)Since PyYAML 5.1, bare yaml.load(f) without a Loader argument emits a warning for exactly this reason. Make safe_load muscle memory and the whole class of vulnerability disappears.
Round-Trip Caveats: What the Conversion Loses
YAML → JSON → YAML is not a lossless journey. The parsed data survives; the human layer does not:
- Comments are gone. They are not part of the data model, so every
#line vanishes at load time. If you must edit YAML programmatically while keeping comments, useruamel.yaml(Python) in round-trip mode instead of PyYAML. - Anchors are flattened. JSON has no reference syntax, so each
*aliasis expanded into a full copy. A compact 50-line file with anchors can explode into hundreds of JSON lines, and converting back does not restore the anchors. - Key order may change. JSON objects are technically unordered; PyYAML additionally sorts keys on dump unless you pass
sort_keys=False. Diffs against the original file become noisy. - Types get normalized. YAML dates become strings,
NaN/Infinityare invalid JSON, and octal or sexagesimal edge cases resolve to plain numbers. - Multiple documents need special handling. JSON has no
---equivalent — emit an array of documents or one file per document.
Command Line: yq One-Liners
For shell scripts and quick checks, yq (the Go version by Mike Farah) is the standard tool — think jq for YAML:
# YAML → JSON yq -o=json config.yaml > config.json # JSON → YAML yq -P config.json > config.yaml # Extract one field while converting yq -o=json '.spec.containers[0].image' deployment.yaml # Convert every doc in a multi-document manifest yq ea -o=json '[.]' all-manifests.yaml # No yq installed? Python one-liner (PyYAML required): python -c "import yaml,json,sys; \ print(json.dumps(yaml.safe_load(sys.stdin), indent=2))" \ < config.yaml > config.json
And when you just need to eyeball how a snippet parses — no terminal, no install — paste it into the free YAML ↔ JSON converter, which runs entirely in your browser.
Choosing Your Approach
- Node app or build script — js-yaml; use
loadAllfor multi-document manifests. - Python service or data pipeline — PyYAML with
safe_load; switch to ruamel.yaml only if comments must survive edits. - Shell scripts and CI steps — yq; it is a single static binary, ideal for containers.
- One-off inspection or debugging — a browser-based converter beats writing any code at all.
- Anything user-uploaded — safe loaders only, size-limit the input, and wrap parsing in a try/except that returns a clean 400 error.
Frequently Asked Questions
yaml.load with the full loader can construct arbitrary Python objects from special YAML tags, which lets a malicious file execute code when parsed. yaml.safe_load only builds plain types — dicts, lists, strings, numbers, booleans — and is what you should use for any file you did not author yourself.
No. Comments are not part of the parsed data model, so they are discarded the moment YAML is loaded. Anchors and aliases are also expanded into full copies, and formatting is lost. If you need to edit YAML while keeping comments, use a round-trip library like ruamel.yaml in Python.
The yq tool does it in one line: yq -o=json file.yaml converts YAML to JSON, and yq -P file.json converts JSON back to YAML. Python also works without installing anything extra if PyYAML is present: python -c "import yaml, json, sys; print(json.dumps(yaml.safe_load(sys.stdin)))" < file.yaml.