YAML and JSON represent the same underlying data — objects, arrays, strings, numbers, booleans, and null. They're just written differently. YAML is designed for humans to write and read; JSON is what most programs, APIs, and config parsers actually consume. Converting between them is a routine task in DevOps, backend development, and any workflow that touches Kubernetes, CI/CD, or configuration management.
This guide covers the practical differences between the two formats, where conversions go wrong, and how to parse YAML and emit JSON in the four most common server-side languages.
The core difference: same data, different syntax
JSON is strict and machine-readable:
{
"service": "api",
"replicas": 3,
"ports": [8080, 443],
"env": {
"NODE_ENV": "production",
"DEBUG": false
}
}
YAML is lenient and human-friendly:
service: api
replicas: 3
ports:
- 8080
- 443
env:
NODE_ENV: production
DEBUG: false
# Comments are allowed here
The underlying data is identical. When you convert YAML to JSON you're not changing the data — you're changing the representation. The key challenges are YAML-specific features that have no JSON equivalent.
What YAML can express that JSON cannot
Before writing conversion code, know what gets lost:
| YAML feature | JSON equivalent | What happens |
|---|---|---|
# comment |
None | Comments are silently dropped |
yes / on / true |
true |
All become boolean true |
no / off / false |
false |
All become boolean false |
~ or null |
null |
Normalised to null |
1_000_000 |
1000000 |
Underscores removed |
| Multi-line strings (` | , >`) |
"line1\nline2" |
Anchors (&) and aliases (*) |
Inlined value | Expanded inline |
Multiple documents (---) |
Needs special handling | Only first doc, or array of docs |
The most dangerous conversion is YAML type coercion. The unquoted value 3.10 becomes the number 3.1 (trailing zero dropped). The unquoted value yes becomes true. Country codes like NO (Norway) and ON (Ontario) can silently flip to booleans. Always quote strings that look like booleans or numbers if you control the YAML source.
JavaScript
In Node.js, use the js-yaml library. It's the de facto standard and handles all YAML 1.2 features.
npm install js-yaml
import { load } from "js-yaml";
import { readFileSync } from "fs";
// From a file
const yaml = readFileSync("config.yaml", "utf8");
const data = load(yaml); // parses YAML → JS object
const json = JSON.stringify(data, null, 2); // JS object → JSON string
console.log(json);
// From a string
const inline = `
name: deploy
steps:
- run: npm test
- run: npm build
`;
console.log(JSON.stringify(load(inline), null, 2));
In the browser, the same js-yaml library works (it's fully isomorphic). Use import { load } from "js-yaml" in a bundled project or load it via a CDN script tag. The Data Converter does exactly this — paste YAML, get JSON instantly, entirely in your browser.
For multi-document YAML files (separated by ---), use loadAll:
import { loadAll } from "js-yaml";
const docs = [];
loadAll(multiDocYaml, (doc) => docs.push(doc));
console.log(JSON.stringify(docs, null, 2)); // array of documents
Python
Python's standard library doesn't include a YAML parser. Install PyYAML:
pip install pyyaml
import yaml
import json
# From a file
with open("config.yaml") as f:
data = yaml.safe_load(f) # parses YAML → Python dict
print(json.dumps(data, indent=2)) # Python dict → JSON string
# From a string
yaml_string = """
server:
host: localhost
port: 8080
debug: false
"""
data = yaml.safe_load(yaml_string)
print(json.dumps(data, indent=2))
Always use yaml.safe_load, never yaml.load. The unsafe loader executes arbitrary Python via YAML tags (!!python/object), which is a remote code execution vulnerability if you parse untrusted input.
For multi-document YAML:
import yaml, json
with open("multi.yaml") as f:
docs = list(yaml.safe_load_all(f)) # list of documents
print(json.dumps(docs, indent=2))
If you need to preserve key insertion order (Python 3.7+ dicts are already ordered), no extra steps are required. Dates parsed from YAML (2024-01-15) become Python datetime.date objects, which are not JSON-serialisable by default. Handle them explicitly:
import datetime
def serialise(obj):
if isinstance(obj, (datetime.date, datetime.datetime)):
return obj.isoformat()
raise TypeError(f"Not serialisable: {type(obj)}")
print(json.dumps(data, indent=2, default=serialise))
Go
Go's standard library has no YAML support. Use gopkg.in/yaml.v3:
go get gopkg.in/yaml.v3
package main
import (
"encoding/json"
"fmt"
"os"
"gopkg.in/yaml.v3"
)
func yamlToJSON(input []byte) ([]byte, error) {
var parsed any
if err := yaml.Unmarshal(input, &parsed); err != nil {
return nil, fmt.Errorf("yaml parse: %w", err)
}
// yaml.v3 produces map[string]any for objects — JSON-serialisable.
return json.MarshalIndent(parsed, "", " ")
}
func main() {
src, _ := os.ReadFile("config.yaml")
out, err := yamlToJSON(src)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
fmt.Println(string(out))
}
One gotcha with yaml.v2 (the older library): it unmarshals maps to map[interface{}]interface{}, which encoding/json refuses to serialise. yaml.v3 returns map[string]any instead, which is JSON-compatible. Prefer yaml.v3 for all new code.
PHP
PHP has no built-in YAML parser. Use the symfony/yaml component:
composer require symfony/yaml
<?php
use Symfony\Component\Yaml\Yaml;
// From a file
$data = Yaml::parseFile('config.yaml');
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
// From a string
$yamlString = <<<YAML
database:
host: localhost
port: 5432
name: myapp
YAML;
$data = Yaml::parse($yamlString);
echo json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
For boolean-like strings, Symfony YAML (like all YAML 1.1 parsers) converts yes/no/on/off to booleans. To disable this and get strict YAML 1.2 parsing, pass Yaml::PARSE_CONSTANT:
$data = Yaml::parse($yamlString, Yaml::PARSE_CONSTANT);
Quick reference
| Language | Parse YAML | Emit JSON | Watch out for |
|---|---|---|---|
| JavaScript | js-yaml: load(str) |
JSON.stringify(obj, null, 2) |
Multi-doc: use loadAll |
| Python | PyYAML: yaml.safe_load(f) |
json.dumps(data, indent=2) |
Dates not JSON-serialisable; never use yaml.load |
| Go | yaml.v3: yaml.Unmarshal(b, &v) |
json.MarshalIndent(v, "", " ") |
Use v3 not v2 (map type) |
| PHP | symfony/yaml: Yaml::parseFile(path) |
json_encode($data, JSON_PRETTY_PRINT) |
yes/no → booleans |
| CLI | yq e -o=json . file.yaml |
— | yq available via brew/apt |
| Browser | Toolmingo Data Converter | — | No upload, runs locally |
6 common pitfalls
1. yes/no silently become booleans. Country codes, feature flag values, and Git branch names can all match YAML 1.1's boolean set. Quote them in the source YAML if you control it, or add explicit handling after parsing.
2. Comments are lost forever. JSON has no comment syntax. Once YAML comments are dropped in conversion, they cannot be recovered by round-tripping back to YAML. Keep the original YAML file if comments matter.
3. Python datetime objects crash json.dumps. YAML dates like 2024-01-15 become Python datetime.date objects, not strings. Pass a default serialiser to json.dumps or convert dates to ISO strings before serialising.
4. Duplicate keys are silently de-duped. YAML technically allows duplicate keys; most parsers keep only the last value. JSON also prohibits duplicate keys. If your YAML source has duplicates (usually a human error), you'll lose data silently.
5. Go yaml.v2 map type breaks JSON. Using the older gopkg.in/yaml.v2 produces map[interface{}]interface{} which encoding/json cannot handle. Switch to yaml.v3 or add a recursive type-conversion function.
6. Numbers with leading zeros are octal in YAML 1.1. The value 0755 in unquoted YAML is parsed as the octal integer 493, not the string "0755". This is a classic pitfall with file permissions in config files. Quote them.
FAQ
Can I convert YAML to JSON without installing a library?
In the browser, yes — the Data Converter runs entirely client-side with no installation needed. On the command line, yq is a single binary that converts YAML to JSON: yq e -o=json . config.yaml.
Will multi-document YAML (separated by ---) work?
Most parsers support it, but you need to call the "load all" variant. In Python it's yaml.safe_load_all(f), in js-yaml it's loadAll(str, callback), and in Go you decode in a loop using yaml.NewDecoder. The result is an array of JSON objects.
What happens to YAML anchors and aliases?
They are resolved before parsing. If &default defines a block and *default references it elsewhere, the parser inlines the referenced value. The output JSON reflects the resolved values, not the anchor/alias syntax.
Why does 3.10 become 3.1 in JSON?
YAML parses 3.10 as the floating-point number 3.1 (trailing zeros are insignificant in numeric literals). JSON then serialises that number as 3.1. If you need to preserve "3.10" as a string — common for version numbers — quote it in the YAML source: version: "3.10".
How do I convert a Kubernetes YAML manifest to JSON?
The same code works. Kubernetes YAML manifests are standard YAML, usually a single document. After parsing and JSON-encoding, the result is a valid Kubernetes JSON manifest that you can apply with kubectl apply -f manifest.json or send to the API directly.
Is it safe to convert YAML online?
Only if the tool runs in your browser. Toolmingo's Data Converter does all processing client-side — your YAML is never uploaded to a server. That matters for manifests referencing internal hostnames, API keys as environment variable names, or any internal service topology.