JSON y YAML son los dos formatos dominantes de serializacion de datos. JSON sobresale para APIs, YAML para archivos de configuracion. YAML es un superconjunto de JSON con comentarios, anclas y cadenas multilínea. Cuidado con las trampas YAML como el problema de Noruega.
- JSON usa llaves y comillas estrictas; YAML usa indentacion con puntuacion minima.
- YAML soporta comentarios (#), anclas/alias (&/*) y cadenas multilinea (| y >).
- El "problema de Noruega": NO, YES, on, off sin comillas se convierten en booleanos en YAML 1.1.
- En Python, siempre usar yaml.safe_load() en lugar de yaml.load().
- Kubernetes, Docker Compose, GitHub Actions usan YAML como formato principal.
- Use js-yaml (JavaScript), PyYAML (Python) o yq (CLI) para la conversion.
Prueba nuestro convertidor JSON-YAML gratuito →
JSON vs YAML: Comparacion de sintaxis
JSON y YAML representan las mismas estructuras con filosofias diferentes.
Los mismos datos en ambos formatos:
{
"server": {
"host": "localhost",
"port": 8080,
"ssl": true
},
"database": {
"name": "myapp",
"replicas": [
"db1.example.com",
"db2.example.com"
]
},
"features": [
"authentication",
"logging",
"rate-limiting"
]
}# Server configuration
server:
host: localhost
port: 8080
ssl: true
# Database settings
database:
name: myapp
replicas:
- db1.example.com
- db2.example.com
features:
- authentication
- logging
- rate-limitingYAML elimina llaves y la mayoria de comillas, usando indentacion para el anidamiento.
Cuando usar JSON vs YAML
La eleccion depende de quien lee y escribe el archivo:
- APIs REST y respuestas GraphQL
- Intercambio de datos entre microservicios
- Bases de datos MongoDB
- Proyectos JavaScript/TypeScript
- Manifiestos como package.json
- Manifiestos Kubernetes
- Configuracion Docker Compose
- Pipelines CI/CD
- Playbooks Ansible
- Archivos de configuracion editados frecuentemente
Anclas, alias y cadenas multilinea YAML
Funcionalidades YAML sin equivalente en JSON.
Anclas y alias
Las anclas (&) definen bloques reutilizables, los alias (*) los referencian:
# Define reusable defaults with an anchor
defaults: &default_db
adapter: postgres
host: localhost
port: 5432
pool_size: 10
# Reference with alias and override specific fields
development:
database:
<<: *default_db # Merge all defaults
database: myapp_dev
pool_size: 5 # Override pool_size
staging:
database:
<<: *default_db
database: myapp_staging
host: staging-db.internal
production:
database:
<<: *default_db
database: myapp_prod
host: prod-db.internal
pool_size: 25
# After JSON conversion (anchors fully expanded):
# {
# "defaults": { "adapter": "postgres", "host": "localhost", "port": 5432, "pool_size": 10 },
# "development": {
# "database": { "adapter": "postgres", "host": "localhost", "port": 5432, "pool_size": 5, "database": "myapp_dev" }
# },
# ...
# }Cadenas multilinea
YAML ofrece dos estilos de bloques escalares:
Bloque literal (|): preserva saltos de linea.
Bloque plegado (>): une lineas con espacios.
# Literal block (|) - preserves newlines exactly
script: |
#!/bin/bash
echo "Starting deployment..."
docker compose pull
docker compose up -d
echo "Done!"
# Folded block (>) - joins lines with spaces
description: >
This is a long description
that spans multiple lines.
Each newline becomes a space
in the resulting string.
# Strip trailing newline with |-
sql_query: |-
SELECT users.name, orders.total
FROM users
JOIN orders ON users.id = orders.user_id
WHERE orders.created_at > '2024-01-01'
# Keep all trailing newlines with |+
message: |+
Line 1
Line 2
(trailing newlines preserved)
# JSON equivalents:
# "script": "#!/bin/bash\necho \"Starting...\n..."
# "description": "This is a long description that spans..."
# "sql_query": "SELECT users.name..." (no trailing \n)Indicadores de chomping: |+ mantiene todos, |- elimina todos.
Conversion en JavaScript (js-yaml)
js-yaml es el parser YAML mas usado para JavaScript:
// npm install js-yaml
const yaml = require('js-yaml');
const fs = require('fs');
// ===== JSON to YAML =====
const jsonData = {
apiVersion: 'apps/v1',
kind: 'Deployment',
metadata: { name: 'web-app', labels: { app: 'web' } },
spec: {
replicas: 3,
selector: { matchLabels: { app: 'web' } },
template: {
spec: {
containers: [{
name: 'app',
image: 'nginx:1.25',
ports: [{ containerPort: 80 }]
}]
}
}
}
};
const yamlOutput = yaml.dump(jsonData, {
indent: 2,
lineWidth: 120,
noRefs: true, // Don't use YAML anchors
sortKeys: false, // Preserve key order
quotingType: '"', // Use double quotes
});
console.log(yamlOutput);
// ===== YAML to JSON =====
const yamlString = fs.readFileSync('config.yaml', 'utf8');
const parsed = yaml.load(yamlString);
const jsonString = JSON.stringify(parsed, null, 2);
fs.writeFileSync('config.json', jsonString);
// ===== Handle multi-document YAML =====
const multiDoc = `
---
name: service-a
port: 3000
---
name: service-b
port: 3001
`;
const docs = [];
yaml.loadAll(multiDoc, (doc) => docs.push(doc));
console.log(JSON.stringify(docs, null, 2));
// [{ "name": "service-a", "port": 3000 }, { "name": "service-b", "port": 3001 }]Para TypeScript, las definiciones de tipo estan incluidas.
Prueba nuestro convertidor JSON-YAML gratuito →
Conversion en Python (PyYAML, ruamel.yaml)
Python tiene dos bibliotecas YAML principales.
PyYAML
# pip install pyyaml
import yaml
import json
# ===== YAML to JSON =====
yaml_text = """
server:
host: localhost
port: 8080
features:
- auth
- logging
database:
name: myapp
ssl: true
"""
# ALWAYS use safe_load (never yaml.load with untrusted input)
data = yaml.safe_load(yaml_text)
json_output = json.dumps(data, indent=2, ensure_ascii=False)
print(json_output)
# ===== JSON to YAML =====
json_text = '{"name": "app", "version": "2.0", "debug": false}'
data = json.loads(json_text)
yaml_output = yaml.dump(data, default_flow_style=False, allow_unicode=True, sort_keys=False)
print(yaml_output)
# ===== File conversion =====
with open('config.yaml', 'r') as yf:
config = yaml.safe_load(yf)
with open('config.json', 'w') as jf:
json.dump(config, jf, indent=2, ensure_ascii=False)
# ===== Multi-document YAML =====
multi_yaml = """
---
name: doc1
value: 100
---
name: doc2
value: 200
"""
docs = list(yaml.safe_load_all(multi_yaml))
print(json.dumps(docs, indent=2))ruamel.yaml (preserva comentarios)
ruamel.yaml preserva comentarios al editar:
# pip install ruamel.yaml
from ruamel.yaml import YAML
from io import StringIO
import json
yaml_handler = YAML()
yaml_handler.preserve_quotes = True
# Load YAML with comments preserved
yaml_text = """
# Application configuration
app:
name: my-service # Service name
port: 3000 # Listen port
debug: false
"""
data = yaml_handler.load(yaml_text)
# Modify a value
data['app']['port'] = 8080
# Write back - comments are preserved!
output = StringIO()
yaml_handler.dump(data, output)
print(output.getvalue())
# Output still has "# Application configuration" and inline comments
# Convert to JSON (comments lost in JSON, but preserved in YAML round-trip)
json_output = json.dumps(dict(data), indent=2, default=str)
print(json_output)Conversion por CLI (yq, jq)
Las herramientas CLI son ideales para conversiones rapidas:
yq: La navaja suiza YAML
yq es un procesador YAML ligero:
# Install yq (Mike Farah version)
# macOS: brew install yq
# Linux: snap install yq OR wget from GitHub releases
# Windows: choco install yq
# ===== YAML to JSON =====
yq -o=json config.yaml
yq -o=json '.' config.yaml > config.json
# ===== JSON to YAML =====
yq -o=yaml config.json
yq -o=yaml -P '.' config.json > config.yaml # -P for pretty print
# ===== Query and filter =====
yq '.server.port' config.yaml # Extract a value
yq '.spec.containers[0].image' deploy.yaml # Array access
yq '.metadata.labels' deploy.yaml # Get nested object
# ===== Modify in-place =====
yq -i '.server.port = 9090' config.yaml
yq -i '.spec.replicas = 5' deploy.yaml
# ===== Merge multiple files =====
yq eval-all 'select(fileIndex == 0) * select(fileIndex == 1)' base.yaml override.yaml
# ===== Convert multi-document YAML to JSON array =====
yq -o=json -s '.' multi-doc.yamljq
jq no maneja YAML nativamente pero se combina con yq:
# Pipe yq output through jq for advanced JSON processing
yq -o=json config.yaml | jq '.server'
yq -o=json deploy.yaml | jq '.spec.template.spec.containers[] | .name'
# Use jq to transform JSON, then convert to YAML
cat data.json | jq '{filtered: .items | map(select(.active))}' | yq -o=yaml -PComandos rapidos
# Python one-liner: YAML to JSON
python3 -c 'import sys,yaml,json; json.dump(yaml.safe_load(sys.stdin),sys.stdout,indent=2)' < config.yaml
# Python one-liner: JSON to YAML
python3 -c 'import sys,yaml,json; print(yaml.dump(json.load(sys.stdin),default_flow_style=False))' < config.json
# Ruby one-liner: YAML to JSON
ruby -ryaml -rjson -e 'puts JSON.pretty_generate(YAML.safe_load(STDIN.read))' < config.yamlManifiestos Kubernetes
Kubernetes hizo de YAML la lengua franca de la infraestructura cloud-native.
Un manifiesto Deployment tipico:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-application
namespace: production
labels:
app: web
version: "2.0" # Quoted to prevent float interpretation
environment: production
annotations:
description: >- # Folded block, strip trailing newline
Production web application deployment
with auto-scaling and health checks
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: myregistry/web-app:2.0.1
ports:
- containerPort: 8080
protocol: TCP
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-credentials
key: url
- name: LOG_LEVEL
value: "info" # Quoted to ensure string
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
---
# Multiple resources in one file
apiVersion: v1
kind: Service
metadata:
name: web-service
spec:
selector:
app: web
ports:
- port: 80
targetPort: 8080
type: ClusterIPPatrones YAML de Kubernetes: mappings anidados, secuencias, cadenas multilinea.
Docker Compose y CI/CD
Docker Compose y GitHub Actions son otro ecosistema YAML importante:
Docker Compose
# docker-compose.yml
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
NODE_ENV: production
ports:
- "3000:3000"
environment:
- DATABASE_URL=postgres://user:pass@db:5432/myapp
- REDIS_URL=redis://cache:6379
depends_on:
db:
condition: service_healthy
cache:
condition: service_started
volumes:
- ./uploads:/app/uploads
restart: unless-stopped
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: myapp
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d myapp"]
interval: 10s
timeout: 5s
retries: 5
cache:
image: redis:7-alpine
command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
volumes:
postgres_data:GitHub Actions
Los workflows de GitHub Actions usan patrones YAML especificos:
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- run: npm ci
- run: npm test
- run: npm run build
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to production
env:
DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
run: |
echo "Deploying to production..."
./scripts/deploy.shPrueba nuestro convertidor JSON-YAML gratuito →
Trampas YAML
La flexibilidad de YAML conlleva trampas:
El problema de Noruega
La trampa mas famosa de YAML:
# YAML 1.1 boolean coercion (PyYAML, many other parsers)
# These ALL become booleans when unquoted:
countries:
- US # String "US" (ok)
- GB # String "GB" (ok)
- NO # BECOMES: false (Norway disappears!)
- FR # String "FR" (ok)
settings:
verbose: yes # BECOMES: true (not the string "yes")
debug: no # BECOMES: false
feature: on # BECOMES: true
legacy: off # BECOMES: false
confirm: y # BECOMES: true
cancel: n # BECOMES: false
# FIX: Always quote ambiguous values
countries:
- "US"
- "GB"
- "NO" # Now correctly a string
- "FR"
settings:
verbose: "yes" # String "yes"
debug: "no" # String "no"El codigo de pais NO se convierte en false. Solucion: usar comillas.
Errores de indentacion
YAML solo usa espacios (nunca tabulaciones). Configurar editor a 2 espacios:
# .editorconfig - enforce consistent YAML formatting
[*.{yml,yaml}]
indent_style = space
indent_size = 2
tab_width = 2
insert_final_newline = true
trim_trailing_whitespace = true
# .yamllint.yml - lint configuration
---
extends: default
rules:
indentation:
spaces: 2
indent-sequences: true
truthy:
check-keys: true
allowed-values: ["true", "false"]
line-length:
max: 120Booleanos inesperados
Numeros de version 1.0 se convierten en flotantes:
# More unexpected type coercions in YAML:
version: 1.0 # BECOMES: float 1.0 (not string "1.0")
version: "1.0" # String "1.0" (correct)
octal: 0o17 # BECOMES: integer 15
hex: 0xFF # BECOMES: integer 255
date: 2024-01-15 # BECOMES: date object (in some parsers)
date: "2024-01-15" # String "2024-01-15" (correct)
null_trap: null # BECOMES: null (not string "null")
null_trap: ~ # ALSO BECOMES: null
null_trap: "" # Empty string (if you want empty, not null)
# Special float values
infinity: .inf # BECOMES: Infinity
not_a_number: .nan # BECOMES: NaN
# Rule of thumb: if it's not obviously a string, quote itSeguridad YAML
Los parsers YAML pueden ser peligrosos.
Patron peligroso: yaml.load(data) puede instanciar objetos arbitrarios.
# DANGEROUS - Never do this with untrusted YAML input!
import yaml
# This YAML payload can execute arbitrary commands:
malicious_yaml = """
!!python/object/apply:os.system
args: ['echo HACKED > /tmp/pwned']
"""
# BAD: yaml.load() with FullLoader allows object construction
# data = yaml.load(malicious_yaml, Loader=yaml.FullLoader) # DANGER!
# GOOD: safe_load() only allows basic types
data = yaml.safe_load(malicious_yaml) # Raises ConstructorError
# ALSO GOOD: ruamel.yaml with safe type
from ruamel.yaml import YAML
safe_yaml = YAML(typ='safe')
data = safe_yaml.load(malicious_yaml) # Raises errorPatron seguro: usar yaml.safe_load().
js-yaml v4 es seguro por defecto.
Limitar tamano de entrada, validar schema, carga segura obligatoria.
Tabla comparativa
Comparacion completa de JSON y YAML:
| Feature | JSON | YAML |
|---|---|---|
| Syntax | Braces {} and brackets [] | Indentation-based |
| Comments | Not supported | Supported with # |
| String Quoting | Required (double quotes) | Optional for most strings |
| Multi-line Strings | Escape with \n | Block scalars: | and > |
| Anchors / Aliases | Not supported | Supported with & and * |
| Multiple Documents | One per file | Yes, separated by --- |
| Data Types | String, Number, Boolean, null, Array, Object | All JSON types + dates, binary, custom tags |
| Parsing Speed | Fast (simple grammar) | Slower (indentation-sensitive) |
| File Size | Larger (quotes, braces) | Smaller (minimal punctuation) |
| Tooling | Universal (every language) | Good (PyYAML, js-yaml, yq) |
| Primary Use | APIs, data exchange | Configuration files |
| Superset Relation | Base format | Superset of JSON |
Preguntas frecuentes
Cual es la diferencia entre JSON y YAML?
JSON usa llaves, YAML indentacion. YAML soporta comentarios y cadenas multilinea.
Como convertir JSON a YAML online?
Pegue su JSON en una herramienta de conversion online.
Es YAML mejor que JSON para configuracion?
Si, para archivos editados frecuentemente por humanos.
Que es el problema de Noruega?
YAML 1.1 interpreta NO como false. Solucion: comillas.
Por que usar yaml.safe_load()?
yaml.load() puede ejecutar codigo arbitrario.
Se preservan los comentarios YAML?
No. Use ruamel.yaml para preservarlos.
Como convertir por CLI?
Use yq -o=json file.yaml.
Que funcionalidades YAML se pierden?
Comentarios, anclas, bloques multilinea, multiples documentos, tipos fecha.
Comprender la relacion entre JSON y YAML es esencial para el desarrollo moderno.
Convierta JSON y YAML al instante con nuestra herramienta gratuita. →