JSON to YAML Conversion: Complete Guide for Developers
Table of Contents
- 1. Why Convert Between JSON and YAML
- 2. Key Differences Between JSON and YAML Syntax
- 3. YAML Features Not Available in JSON
- 4. Common Conversion Gotchas and Edge Cases
- 5. Converting JSON to YAML in Different Languages
- 6. Command-Line Tools for Conversion
- 7. YAML Best Practices for Config Files
- 8. When to Use JSON vs YAML
- 9. Real-World Examples
Convert JSON to YAML Instantly
Need a quick conversion? Use our JSON to YAML Converter to transform your data in one click, directly in your browser. No installation required.
1. Why Convert Between JSON and YAML
JSON and YAML are two of the most widely used data serialization formats in software development. While they can represent the same data structures, they serve different niches in the development ecosystem, and you will frequently need to convert between them.
Here are the most common scenarios that call for JSON-to-YAML conversion (or vice versa):
- Kubernetes and Docker: You receive API responses in JSON but need to write manifests in YAML. The Kubernetes API returns JSON by default, yet every tutorial and best practice guide uses YAML for resource definitions.
- CI/CD pipelines: GitHub Actions, GitLab CI, and CircleCI all use YAML for pipeline configuration. If you generate pipeline configs programmatically from JSON data, you need reliable conversion.
- Configuration migration: Moving from a JSON-based config system (like
package.jsonortsconfig.json) to a YAML-based one (like a Helm chart or Ansible playbook) requires accurate conversion. - API debugging: REST APIs return JSON, but YAML is often easier to read for deeply nested structures. Converting API responses to YAML can make debugging faster.
- Documentation: YAML's comment support makes it a better choice for config file examples in documentation. Converting a JSON schema to YAML lets you annotate every field.
- Team preferences: Some teams standardize on YAML for human-edited files and JSON for machine-generated ones. Interoperability between the two is essential.
Understanding the relationship between these formats, and knowing how to convert between them correctly, is a practical skill that every developer working with modern infrastructure tools needs.
2. Key Differences Between JSON and YAML Syntax
JSON and YAML can represent the same data structures (objects, arrays, strings, numbers, booleans, and null), but their syntax differs significantly. Understanding these differences is essential for accurate manual or programmatic conversion.
Objects / Mappings
JSON uses curly braces and colons with quoted keys. YAML uses indentation and colons without braces or quotes:
{
"name": "web-app",
"version": "2.1.0",
"private": true
}
name: web-app
version: "2.1.0"
private: true
Notice that YAML does not require quotes around most strings. However, the version string "2.1.0" is quoted in the YAML example to prevent it from being interpreted as a number. This is one of the subtleties that trips people up during conversion.
Arrays / Sequences
JSON uses square brackets. YAML uses dashes with indentation:
{
"fruits": ["apple", "banana", "cherry"]
}
fruits:
- apple
- banana
- cherry
YAML also supports an inline flow syntax that looks identical to JSON: fruits: [apple, banana, cherry]. This can be useful for short lists.
Nested Structures
JSON nests with braces and brackets. YAML nests with indentation levels:
{
"database": {
"host": "localhost",
"port": 5432,
"credentials": {
"user": "admin",
"password": "secret"
}
}
}
database:
host: localhost
port: 5432
credentials:
user: admin
password: secret
Data Types
Both formats support strings, numbers, booleans, null, arrays, and objects. However, YAML's type inference is more aggressive:
| Feature | JSON | YAML |
|---|---|---|
| Strings | Always double-quoted | Usually unquoted; single or double quotes optional |
| Numbers | Integer or float | Integer, float, octal (0o14), hex (0xFF) |
| Booleans | true, false |
true, false, yes, no, on, off (YAML 1.1) |
| Null | null |
null, ~, or empty value |
| Dates | String only (no native date type) | Native date parsing (2026-02-11) |
| Comments | Not supported | # inline and full-line comments |
| Multi-document | Not supported | Supported via --- separator |
Validate Your JSON Before Converting
Malformed JSON will produce broken YAML. Always validate first with our JSON Validator to catch syntax errors before conversion.
3. YAML Features Not Available in JSON
YAML is a superset of JSON (every valid JSON document is also valid YAML), but YAML offers several features that have no JSON equivalent. When converting YAML to JSON, these features must be resolved or lost.
Comments
YAML supports comments with the # character. This is one of the most-cited reasons developers prefer YAML for configuration files:
# Database configuration
database:
host: localhost # Use 127.0.0.1 in Docker
port: 5432 # Default PostgreSQL port
max_connections: 100 # Increase for production
# SSL settings (uncomment in production)
# ssl: true
# ssl_cert: /etc/ssl/db.pem
When converting this to JSON, all comments are stripped. There is no way to preserve them. This is a one-way loss: JSON has no comment syntax.
Anchors and Aliases
YAML supports anchors (&) and aliases (*) for reusing values and avoiding duplication:
defaults: &defaults
adapter: postgres
host: localhost
port: 5432
development:
database:
<<: *defaults
database: myapp_dev
production:
database:
<<: *defaults
database: myapp_prod
host: db.example.com
When converting to JSON, anchors and aliases are resolved into their expanded form. The resulting JSON will have duplicated values everywhere the anchor was referenced. The DRY (Don't Repeat Yourself) benefit is lost:
{
"defaults": {
"adapter": "postgres",
"host": "localhost",
"port": 5432
},
"development": {
"database": {
"adapter": "postgres",
"host": "localhost",
"port": 5432,
"database": "myapp_dev"
}
},
"production": {
"database": {
"adapter": "postgres",
"host": "localhost",
"port": 5432,
"database": "myapp_prod",
"host": "db.example.com"
}
}
}
Multi-line Strings
YAML provides two block scalar styles for multi-line strings that are far more readable than JSON's escaped newlines:
Literal block scalar (|): Preserves newlines exactly as written.
script: |
#!/bin/bash
echo "Starting deployment..."
docker build -t myapp .
docker push myapp:latest
kubectl apply -f deploy.yaml
Folded block scalar (>): Folds newlines into spaces (like a paragraph), preserving blank lines as actual newlines.
description: >
This is a long description that spans
multiple lines but will be folded into
a single paragraph when parsed.
A blank line creates a real newline.
In JSON, the equivalent requires explicit \n escape sequences crammed into a single string:
{
"script": "#!/bin/bash\necho \"Starting deployment...\"\ndocker build -t myapp .\ndocker push myapp:latest\nkubectl apply -f deploy.yaml\n"
}
Merge Keys
The merge key (<<) lets you merge one mapping into another, combined with anchors:
base_service: &base
restart: always
networks:
- backend
web:
<<: *base
image: nginx:latest
ports:
- "80:80"
api:
<<: *base
image: node:20
ports:
- "3000:3000"
This is extremely useful in Docker Compose files and CI/CD configurations where services share common settings. Converting to JSON expands every merge into the full set of duplicated keys.
Multiple Documents
A single YAML file can contain multiple documents separated by ---:
---
apiVersion: v1
kind: Service
metadata:
name: web
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
JSON has no equivalent. Converting a multi-document YAML file to JSON requires either splitting into multiple JSON files or wrapping them in an array.
4. Common Conversion Gotchas and Edge Cases
Converting between JSON and YAML is usually straightforward, but several edge cases can introduce subtle bugs if you are not careful.
The Norway Problem (Boolean Coercion)
In YAML 1.1 (used by many older parsers, including PyYAML by default), the bare strings yes, no, on, off, y, and n are interpreted as booleans. This is the infamous "Norway problem": the country code NO for Norway becomes false.
# YAML 1.1 interpretation:
countries:
- GB # string "GB"
- FR # string "FR"
- NO # boolean false!
- DE # string "DE"
The fix is to quote the value: "NO". YAML 1.2 (the current spec) removes most of these boolean aliases, accepting only true and false. However, many tools still use YAML 1.1 parsers.
When converting JSON to YAML, any string value that matches a YAML boolean keyword must be quoted in the output. A good converter handles this automatically.
Numeric String Coercion
YAML auto-parses values that look like numbers. The string "1.0" in JSON becomes the float 1.0 in YAML if not quoted:
# This YAML:
version: 1.0
# Parses as the number 1.0, not the string "1.0"
# In JSON it becomes: {"version": 1} (or 1.0)
Version numbers, zip codes, phone numbers, and any string that happens to look numeric must be quoted in YAML:
version: "1.0"
zip_code: "01onal"
phone: "+1-555-0123"
Date String Coercion
YAML 1.1 parsers interpret strings like 2026-02-11 as date objects rather than strings:
# This might become a date object, not a string
release_date: 2026-02-11
# Quote it to keep it as a string
release_date: "2026-02-11"
Indentation Sensitivity
YAML is indentation-sensitive, and inconsistent indentation causes parsing failures. Unlike Python, which requires consistent indentation within a block, YAML requires consistent indentation within each level but allows different amounts per level. Most teams standardize on 2-space indentation:
# Valid but confusing (mixed indent widths):
root:
level1:
level2: value
# Better (consistent 2-space indent):
root:
level1:
level2: value
When generating YAML from JSON programmatically, always specify a consistent indent width.
Special Characters in Keys
JSON keys are always quoted strings, so they can contain any characters. YAML keys are usually unquoted, which means certain characters require quoting:
# These YAML keys need quoting:
"key: with colon": value
"key with #hash": value
"{bracket key}": value
"null": actual string null
Trailing Whitespace and Newlines
YAML block scalars have chomp indicators that control trailing newlines: |+ (keep all), |- (strip all), and | (clip to single newline). A round-trip conversion through JSON may alter trailing whitespace because JSON strings have no such mechanism.
Validate Your YAML Output
After converting, always validate the result. Our YAML Validator catches indentation errors, type coercion issues, and syntax problems before they cause deployment failures.
5. Converting JSON to YAML in Different Languages
Every major programming language has mature libraries for JSON-to-YAML conversion. Below are practical examples for the four most common languages used in DevOps and backend development.
JavaScript / Node.js
The js-yaml package is the standard YAML library for JavaScript:
// Install: npm install js-yaml
const yaml = require('js-yaml');
const fs = require('fs');
// JSON string to YAML
const jsonString = `{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "web-app",
"labels": {
"app": "web"
}
},
"spec": {
"replicas": 3
}
}`;
const jsonObj = JSON.parse(jsonString);
const yamlString = yaml.dump(jsonObj, {
indent: 2,
lineWidth: 120,
noRefs: true, // Don't use anchors/aliases
sortKeys: false, // Preserve key order
quotingType: '"', // Use double quotes when quoting
forceQuotes: false // Only quote when necessary
});
console.log(yamlString);
// Output:
// apiVersion: apps/v1
// kind: Deployment
// metadata:
// name: web-app
// labels:
// app: web
// spec:
// replicas: 3
// YAML to JSON
const yamlInput = fs.readFileSync('config.yaml', 'utf8');
const parsed = yaml.load(yamlInput);
const jsonOutput = JSON.stringify(parsed, null, 2);
console.log(jsonOutput);
// YAML to JSON with schema safety (recommended)
const safeParsed = yaml.load(yamlInput, {
schema: yaml.JSON_SCHEMA // Strict JSON-compatible types only
});
Python
Python has two main YAML libraries. PyYAML is the classic choice; ruamel.yaml is the modern alternative with better YAML 1.2 support:
# Install: pip install pyyaml
import json
import yaml
# JSON to YAML
json_data = '''
{
"services": {
"web": {
"image": "nginx:latest",
"ports": ["80:80", "443:443"],
"environment": {
"NODE_ENV": "production",
"DEBUG": "false"
}
},
"db": {
"image": "postgres:16",
"volumes": ["pgdata:/var/lib/postgresql/data"]
}
}
}
'''
data = json.loads(json_data)
yaml_output = yaml.dump(data,
default_flow_style=False, # Block style (not inline)
indent=2,
sort_keys=False, # Preserve insertion order
allow_unicode=True
)
print(yaml_output)
# YAML to JSON
with open('docker-compose.yaml', 'r') as f:
yaml_data = yaml.safe_load(f) # Always use safe_load!
json_output = json.dumps(yaml_data, indent=2)
print(json_output)
# Using ruamel.yaml for YAML 1.2 compliance
# Install: pip install ruamel.yaml
from ruamel.yaml import YAML
from io import StringIO
ry = YAML()
ry.default_flow_style = False
ry.indent(mapping=2, sequence=4, offset=2)
data = json.loads(json_data)
stream = StringIO()
ry.dump(data, stream)
print(stream.getvalue())
Important: Always use yaml.safe_load() instead of yaml.load(). The latter can execute arbitrary Python code from specially crafted YAML files, which is a serious security vulnerability.
Go
Go's most popular YAML library is gopkg.in/yaml.v3:
package main
import (
"encoding/json"
"fmt"
"log"
"gopkg.in/yaml.v3"
)
func main() {
// JSON to YAML
jsonData := []byte(`{
"apiVersion": "v1",
"kind": "ConfigMap",
"metadata": {
"name": "app-config"
},
"data": {
"database_url": "postgres://localhost:5432/mydb",
"cache_ttl": "300",
"debug": "false"
}
}`)
var obj map[string]interface{}
if err := json.Unmarshal(jsonData, &obj); err != nil {
log.Fatal(err)
}
yamlData, err := yaml.Marshal(obj)
if err != nil {
log.Fatal(err)
}
fmt.Println(string(yamlData))
// YAML to JSON
yamlInput := []byte(`
name: my-service
replicas: 3
ports:
- 8080
- 8443
env:
LOG_LEVEL: info
TIMEOUT: "30s"
`)
var result map[string]interface{}
if err := yaml.Unmarshal(yamlInput, &result); err != nil {
log.Fatal(err)
}
jsonOutput, err := json.MarshalIndent(result, "", " ")
if err != nil {
log.Fatal(err)
}
fmt.Println(string(jsonOutput))
}
Ruby
Ruby includes YAML support in its standard library via Psych:
require 'json'
require 'yaml'
# JSON to YAML
json_string = '{
"name": "rails-app",
"database": {
"adapter": "postgresql",
"host": "localhost",
"port": 5432,
"pool": 25
},
"redis": {
"url": "redis://localhost:6379/0",
"timeout": 5
}
}'
data = JSON.parse(json_string)
yaml_output = data.to_yaml
puts yaml_output
# YAML to JSON
yaml_data = YAML.safe_load(File.read('config.yaml'))
json_output = JSON.pretty_generate(yaml_data)
puts json_output
# Fine-grained control with Psych
yaml_custom = Psych.dump(data,
indentation: 2,
line_width: 120
)
puts yaml_custom
Format Your JSON First
Minified JSON is hard to review before conversion. Use our JSON Formatter to pretty-print and organize your JSON data before converting to YAML.
6. Command-Line Tools for Conversion
Command-line tools are indispensable for scripting, CI/CD pipelines, and quick one-off conversions. Here are the best options.
yq: The YAML Swiss Army Knife
yq (by Mike Farah) is the go-to CLI tool for YAML manipulation. It mirrors jq's syntax and works seamlessly with both JSON and YAML:
# Install
brew install yq # macOS
sudo snap install yq # Ubuntu
choco install yq # Windows
# JSON to YAML
yq -P input.json # Pretty-print as YAML
yq -P '.' input.json > output.yaml # Save to file
# YAML to JSON
yq -o=json input.yaml # Output as JSON
yq -o=json '.' input.yaml > output.json
# Convert from stdin (pipe from curl, etc.)
curl -s https://api.example.com/data | yq -P
# Extract specific fields during conversion
yq -P '.metadata.name' input.json
# Merge multiple YAML files
yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' \
base.yaml override.yaml
Python One-Liners
If Python is installed (it usually is), you can convert without installing any extra tools:
# JSON to YAML (requires PyYAML: pip install pyyaml)
python3 -c "
import sys, json, yaml
print(yaml.dump(json.load(sys.stdin), default_flow_style=False))
" < input.json > output.yaml
# YAML to JSON
python3 -c "
import sys, json, yaml
print(json.dumps(yaml.safe_load(sys.stdin), indent=2))
" < input.yaml > output.json
# Inline conversion from a string
echo '{"name":"app","port":3000}' | python3 -c "
import sys, json, yaml
print(yaml.dump(json.load(sys.stdin), default_flow_style=False))
"
Ruby One-Liners
Ruby's standard library includes both JSON and YAML, so no gem installation is needed:
# JSON to YAML
ruby -ryaml -rjson -e \
'puts YAML.dump(JSON.parse(STDIN.read))' < input.json
# YAML to JSON
ruby -ryaml -rjson -e \
'puts JSON.pretty_generate(YAML.safe_load(STDIN.read))' < input.yaml
jq + yq Pipeline
Combine jq for JSON manipulation with yq for YAML output:
# Extract and transform JSON, output as YAML
cat data.json | jq '{name: .metadata.name, replicas: .spec.replicas}' | yq -P
# Process multiple JSON files into a single YAML
jq -s '.' file1.json file2.json | yq -P
# Convert Kubernetes JSON output to clean YAML
kubectl get deployment web -o json | \
jq 'del(.metadata.managedFields, .status)' | \
yq -P
Node.js One-Liner
# Requires: npm install -g js-yaml
js-yaml input.json > output.yaml
# Or using npx (no global install)
npx js-yaml input.json
Comparison of CLI Tools
| Tool | Install Size | Speed | Best For |
|---|---|---|---|
| yq | ~10 MB (single binary) | Fast | General-purpose YAML/JSON conversion and manipulation |
| python3 -c | 0 (uses system Python) | Moderate | Environments where Python is pre-installed |
| ruby -e | 0 (uses system Ruby) | Moderate | Ruby-centric environments (Rails, Chef) |
| js-yaml CLI | ~1 MB (npm package) | Fast | Node.js projects |
7. YAML Best Practices for Config Files
Once you have converted your JSON data to YAML, follow these best practices to write clean, maintainable YAML configuration files.
1. Use Consistent Indentation (2 Spaces)
The YAML community has standardized on 2-space indentation. Kubernetes, Docker Compose, GitHub Actions, and most other tools expect it:
# Good: 2-space indent
services:
web:
image: nginx
ports:
- "80:80"
# Bad: tabs or inconsistent spacing
services:
web:
image: nginx
2. Quote Strings That Look Like Other Types
Prevent type coercion bugs by quoting ambiguous values:
# Always quote these:
version: "1.0" # Prevents float interpretation
country: "NO" # Prevents boolean false
zip: "01234" # Prevents octal interpretation
enabled: "yes" # Prevents boolean true (YAML 1.1)
timestamp: "12:30" # Prevents sexagesimal number
3. Add Comments Generously
Comments are YAML's biggest advantage over JSON. Use them to explain non-obvious configuration choices:
# Maximum number of worker processes.
# Set to CPU count for compute-bound workloads,
# or higher for I/O-bound workloads.
workers: 4
# Connection timeout in seconds.
# Increase this if you see 504 errors under load.
timeout: 30
4. Use Block Style for Readability
Prefer block style (one item per line) over flow style (inline) for anything more than 2-3 simple items:
# Good: block style for longer lists
environment:
- NODE_ENV=production
- LOG_LEVEL=info
- DATABASE_URL=postgres://db:5432/app
- REDIS_URL=redis://cache:6379
# Acceptable: flow style for very short lists
ports: [80, 443]
5. Avoid Deeply Nested Structures
If your YAML is nested more than 4-5 levels deep, consider restructuring. Deep nesting makes files hard to read and prone to indentation errors:
# Too deep - hard to maintain
app:
server:
http:
routes:
api:
v1:
users:
endpoint: /api/v1/users
# Better - flatter structure
routes:
api_v1_users:
path: /api/v1/users
method: GET
6. Use Anchors for DRY Configuration
When multiple sections share the same values, use anchors to avoid repetition:
x-logging: &logging
driver: json-file
options:
max-size: "10m"
max-file: "3"
services:
web:
image: nginx
logging: *logging
api:
image: node:20
logging: *logging
7. Use Multi-line Strings for Long Values
# Use literal block (|) for preserving newlines
nginx_config: |
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend:3000;
}
}
# Use folded block (>) for paragraphs
description: >
This application provides a REST API for managing
user accounts, authentication tokens, and session
data across multiple microservices.
8. Validate Before Deploying
Always validate YAML files before using them in production. A single indentation error can cause a deployment failure:
# Using yq to validate
yq '.' config.yaml > /dev/null && echo "Valid" || echo "Invalid"
# Using Python
python3 -c "import yaml; yaml.safe_load(open('config.yaml'))"
# Using yamllint (most thorough)
pip install yamllint
yamllint config.yaml
Validate YAML Online
Paste your YAML into our YAML Validator for instant syntax checking with detailed error messages and line numbers.
8. When to Use JSON vs YAML
JSON and YAML each have strengths that make them better suited for different use cases. Choosing the wrong format creates friction; choosing the right one makes your workflow smoother.
Use JSON When:
- Building APIs: JSON is the universal language of REST APIs. Every HTTP client and server framework has native JSON support. Parsing is fast and unambiguous.
- Machine-generated configuration: When config files are written by programs (not humans), JSON's strict syntax prevents ambiguity. There are no type coercion surprises.
- Strict data interchange: JSON's limited type system (string, number, boolean, null, array, object) means the sender and receiver always agree on data types.
- Performance matters: JSON parsers are typically 2-10x faster than YAML parsers because JSON's grammar is simpler. For high-throughput data processing, this difference matters.
- Browser environments:
JSON.parse()andJSON.stringify()are built into every browser. No library needed. - Strict validation is needed: JSON Schema is a mature, widely-supported validation standard. YAML has no equivalent.
Use YAML When:
- Human-edited configuration: YAML's clean syntax, optional quotes, and comment support make it ideal for files that developers read and edit by hand.
- Infrastructure as Code: Kubernetes, Docker Compose, Ansible, Terraform (HCL is similar), GitHub Actions, and most DevOps tools use YAML as their primary format.
- Documentation and examples: YAML's comments let you explain every field inline, making it perfect for sample configuration files.
- DRY configuration: Anchors and aliases eliminate duplication across large config files with repeated patterns.
- Multi-document files: When you need to store multiple related resources in a single file (like Kubernetes manifests), YAML's
---separator is the standard approach.
Decision Matrix
| Criterion | JSON | YAML |
|---|---|---|
| Parse speed | Faster (simpler grammar) | Slower (complex grammar) |
| Human readability | Good (with pretty-print) | Excellent (minimal syntax noise) |
| Human writability | Moderate (brackets, quotes required) | Excellent (minimal ceremony) |
| Comments | Not supported | Supported |
| Type safety | Explicit (no coercion) | Implicit (type inference can surprise) |
| Tooling maturity | Excellent (ubiquitous) | Good (growing) |
| File size | Larger (syntax overhead) | Smaller (less punctuation) |
| Security | Safe (JSON.parse is sandboxed) |
Risky if using unsafe loaders |
For a deeper comparison including TOML, read our JSON vs YAML vs TOML guide. You can also validate your TOML files with our TOML Validator.
9. Real-World Examples
Let's walk through real-world configuration files that you encounter every day, showing the JSON and YAML representations side by side.
Kubernetes Deployment
Kubernetes is where most developers first encounter YAML. The API server speaks JSON, but manifests are almost always written in YAML:
{
"apiVersion": "apps/v1",
"kind": "Deployment",
"metadata": {
"name": "web-app",
"labels": {
"app": "web"
}
},
"spec": {
"replicas": 3,
"selector": {
"matchLabels": {
"app": "web"
}
},
"template": {
"metadata": {
"labels": {
"app": "web"
}
},
"spec": {
"containers": [
{
"name": "nginx",
"image": "nginx:1.27",
"ports": [
{
"containerPort": 80
}
]
}
]
}
}
}
}
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
labels:
app: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
The YAML version is 18 lines versus 35 lines of JSON. With Kubernetes manifests often containing hundreds of lines, this reduction in visual clutter is significant. The ability to add comments explaining resource limits, environment variables, and volume mounts makes YAML the clear winner here.
Docker Compose
Docker Compose files showcase YAML's anchors and merge keys:
# docker-compose.yaml
x-common-env: &common-env
LOG_LEVEL: info
TZ: UTC
x-healthcheck: &healthcheck
interval: 30s
timeout: 10s
retries: 3
services:
web:
image: nginx:latest
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
environment:
<<: *common-env
WORKER_PROCESSES: "4"
healthcheck:
<<: *healthcheck
test: ["CMD", "curl", "-f", "http://localhost/health"]
depends_on:
api:
condition: service_healthy
api:
build:
context: ./api
dockerfile: Dockerfile
environment:
<<: *common-env
DATABASE_URL: postgres://db:5432/app
REDIS_URL: redis://cache:6379
healthcheck:
<<: *healthcheck
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
db:
image: postgres:16
environment:
POSTGRES_DB: app
POSTGRES_USER: admin
POSTGRES_PASSWORD: secret # Use Docker secrets in production
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
<<: *healthcheck
test: ["CMD-SHELL", "pg_isready -U admin"]
volumes:
pgdata:
The &common-env anchor and *common-env alias pattern keeps environment variables consistent across services without duplication. This has no JSON equivalent.
GitHub Actions CI/CD
# .github/workflows/ci.yaml
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
# Cancel in-progress runs for the same branch
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test -- --coverage
- name: Upload coverage
if: matrix.node-version == 20
uses: codecov/codecov-action@v4
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to production
run: |
echo "Deploying to production..."
./scripts/deploy.sh
Notice the multi-line string (run: |) for the deploy script. In JSON, those newlines would need \n escaping, making the config much harder to read and edit.
Ansible Playbook
# deploy-app.yaml
---
- name: Deploy web application
hosts: webservers
become: true
vars:
app_name: myapp
app_port: 3000
node_version: "20" # Quoted to prevent float interpretation
tasks:
- name: Install Node.js
apt:
name: "nodejs={{ node_version }}.*"
state: present
update_cache: true
- name: Clone repository
git:
repo: https://github.com/org/myapp.git
dest: "/opt/{{ app_name }}"
version: main
- name: Install npm dependencies
npm:
path: "/opt/{{ app_name }}"
production: true
- name: Create systemd service
template:
src: app.service.j2
dest: "/etc/systemd/system/{{ app_name }}.service"
notify: restart app
handlers:
- name: restart app
systemd:
name: "{{ app_name }}"
state: restarted
daemon_reload: true
package.json vs pubspec.yaml
Some ecosystems use JSON (Node.js), while others use YAML (Flutter/Dart). The same dependency information looks different in each:
{
"name": "my-app",
"version": "2.1.0",
"description": "A web app",
"scripts": {
"start": "node server.js",
"test": "jest --coverage",
"build": "webpack --mode production"
},
"dependencies": {
"express": "^4.18.0",
"pg": "^8.11.0"
},
"devDependencies": {
"jest": "^29.7.0",
"webpack": "^5.90.0"
}
}
name: my_app
version: "2.1.0" # Quoted!
description: A mobile app
# Production dependencies
dependencies:
flutter:
sdk: flutter
http: ^1.2.0
provider: ^6.1.0
# Development dependencies
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
Notice how the YAML version can include comments explaining dependency groups, while the JSON version cannot.
GitLab CI/CD
# .gitlab-ci.yml
stages:
- build
- test
- deploy
# Shared configuration using YAML anchors
.default_rules: &default_rules
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
build:
stage: build
image: node:20-alpine
<<: *default_rules
script:
- npm ci --cache .npm
- npm run build
artifacts:
paths:
- dist/
expire_in: 1 hour
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- .npm/
test:
stage: test
image: node:20-alpine
<<: *default_rules
script:
- npm ci --cache .npm
- npm test
coverage: '/Statements\s*:\s*(\d+\.?\d*)%/'
deploy_production:
stage: deploy
image: alpine:latest
rules:
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
when: manual
script:
- apk add --no-cache curl
- |
curl -X POST "$DEPLOY_WEBHOOK" \
-H "Authorization: Bearer $DEPLOY_TOKEN" \
-d "{\"version\": \"$CI_COMMIT_SHA\"}"
environment:
name: production
url: https://myapp.example.com
The .default_rules hidden key (YAML keys starting with a dot are ignored by GitLab CI) combined with the anchor pattern is a powerful way to share configuration across jobs.
Conclusion
JSON and YAML are complementary formats, not competitors. JSON excels at machine-to-machine communication with its strict, unambiguous syntax and blazing-fast parsers. YAML excels at human-to-machine communication with its clean syntax, comment support, and powerful features like anchors and multi-line strings.
The key takeaways for converting between them:
- JSON to YAML is lossless: Every JSON document converts perfectly to YAML because YAML is a superset of JSON.
- YAML to JSON is lossy: Comments, anchors, merge keys, and multi-document structure are lost when converting to JSON.
- Watch for type coercion: YAML's automatic type inference can turn strings into booleans, numbers, or dates. Always quote ambiguous values.
- Use the right tool:
yqfor CLI work,js-yamlfor JavaScript,PyYAMLorruamel.yamlfor Python, andyaml.v3for Go. - Always validate: Whether you convert manually or programmatically, validate the output before deploying it.
With the knowledge from this guide, you can confidently convert between JSON and YAML in any context, whether you are writing Kubernetes manifests, configuring CI/CD pipelines, or building tools that bridge the gap between APIs and configuration files.
Try Our JSON to YAML Converter
Put this knowledge into practice with our JSON to YAML Converter. Paste your JSON, get clean YAML output instantly. You can also validate the result with our YAML Validator and format your source with the JSON Formatter.