Migrating JSON Config to YAML: A Step-by-Step Guide
Why Migrate from JSON to YAML?
JSON works fine for machine-to-machine communication, but configuration files written by humans benefit from YAML's readability. YAML supports comments, multi-line strings, and avoids the trailing-comma problem that plagues JSON editing.
Common motivations for migration:
- Adding comments to explain configuration decisions
- Reducing syntax noise — no braces or quotes needed for simple values
- Using YAML anchors to deduplicate repeated configuration blocks
- Better multi-line string handling for embedded scripts and templates
JSON vs. YAML: Structural Differences
| Feature | JSON | YAML |
|---|---|---|
| Comments | Not supported | # line comments |
| Quotes on strings | Required | Optional for simple values |
| Trailing commas | Syntax error | Not applicable |
| Multi-line strings | \n escapes only | Literal and folded block styles |
| Anchors/aliases | Not supported | &anchor and *alias |
| Data types | String, number, bool, null, array, object | Same + date, timestamp, binary, sets |
| Root structure | Object or array | Any type (including scalar) |
Step-by-Step Migration Process
Step 1: Audit Your JSON Files
Before converting anything, identify every JSON config file and its consumers. Ask:
- Which applications read this file?
- Do any tools write back to this file automatically?
- Are there JSON schemas that validate these files?
Applications that both read and write the config may not support YAML output, which blocks full migration.
Step 2: Choose Your Conversion Method
Manual conversion: Open the JSON file, reformat to YAML syntax by hand. Feasible only for small files (under 50 lines).
Command-line tools: Use yq or jq for automated conversion:
# Using yq (recommended)
yq -y '.' config.json > config.yaml
# Using jq + python
jq '.' config.json | python3 -c "import sys, yaml, json; yaml.dump(json.load(sys.stdin), sys.stdout, default_flow_style=False)"
Online converters: Paste JSON, get YAML. Quick for one-off conversions but not suitable for sensitive configs.
Step 3: Convert the Syntax
Key transformations to apply:
{
"database": {
"host": "localhost",
"port": 5432,
"name": "myapp_production",
"ssl": true
},
"features": ["auth", "logging", "metrics"]
}
Becomes:
database:
host: localhost
port: 5432
name: myapp_production
ssl: true
features:
- auth
- logging
- metrics
Step 4: Add Comments and Documentation
Now that YAML supports comments, annotate non-obvious values:
database:
host: localhost # Override with DB_HOST env var in production
port: 5432 # Default PostgreSQL port
name: myapp_production
ssl: true # Required for RDS instances
Step 5: Update Application Code
Modify your application to read YAML instead of JSON:
// Node.js: Before
const config = require('./config.json')
// Node.js: After (using js-yaml)
const fs = require('fs')
const yaml = require('js-yaml')
const config = yaml.load(fs.readFileSync('./config.yaml', 'utf8'))
# Python: Before
import json
with open('config.json') as f:
config = json.load(f)
# Python: After
import yaml
with open('config.yaml') as f:
config = yaml.safe_load(f)
Always use safe_load in Python to avoid executing arbitrary YAML tags.
Step 6: Validate and Test
- Run your test suite against the new YAML config
- Verify that all values parsed correctly — YAML's type coercion can surprise you
- Check that boolean values are correct (
true/false, not"true"/"false") - Confirm numeric strings haven't been converted to numbers unexpectedly
Common YAML Parsing Surprises
Boolean Coercion
YAML treats several values as booleans:
| YAML Value | Parsed As |
|---|---|
true, True, TRUE, yes, Yes, YES, on, On, ON | true |
false, False, FALSE, no, No, NO, off, Off, OFF | false |
If you have a country code NO (Norway), YAML parses it as false. Quote it: "NO".
Numeric Strings
version: 1.10 # Parsed as float 1.1, not string "1.10"
port: 08080 # Parsed as 8080 octal → 4160 decimal
Quote values that must remain strings: version: "1.10", port: "08080".
Indentation Errors
YAML is indentation-sensitive. Mixing tabs and spaces breaks parsing. Configure your editor to use spaces only.
Partial Migration Strategy
If some consumers can't read YAML, run both formats in parallel:
- Maintain YAML as the source of truth
- Generate JSON from YAML in your build step:
yq -j '.' config.yaml > config.json - Commit only the YAML file; git-ignore the generated JSON
This keeps your canonical config in readable YAML while supporting legacy consumers.
Key Takeaways
- YAML adds comments, cleaner syntax, and anchors — worthwhile for human-authored configs
- Audit all consumers before migrating; some tools may not support YAML output
- Use
yqfor automated conversion or Python'syaml.dumpfor programmatic conversion - Watch for YAML's aggressive type coercion: quote strings that look like booleans or numbers
- Always use
safe_load(Python) or equivalent to avoid executing arbitrary YAML tags - For partial migrations, generate JSON from YAML in your build step
Related Guides
Try It Yourself
Convert JSON to YAML (and back) instantly with our free JSON-YAML Converter. Paste your data and switch formats in one click — no installation required.