JSON 和 YAML 是现代开发中两种主流数据序列化格式。JSON 擅长 API 和机器间通信,YAML 则在人工编辑的配置文件中表现出色。YAML 是 JSON 的超集,支持注释、锚点、多行字符串和更简洁的语法。注意 YAML 的陷阱:挪威问题(NO 变成 false)、布尔值强制转换和缩进错误。使用我们的免费在线工具即时转换 JSON 和 YAML。
- JSON 使用大括号和方括号,严格引用;YAML 使用缩进,标点符号最少,对人类更友好。
- YAML 支持注释(#)、锚点/别名(&/*)、多行字符串(| 和 >),这些 JSON 完全没有。
- "挪威问题":未引用的 NO、YES、on、off 在 YAML 1.1 中会变成布尔值。务必引用模糊字符串。
- 在 Python 中,始终使用 yaml.safe_load() 而非 yaml.load(),以防止不受信任的 YAML 执行任意代码。
- Kubernetes、Docker Compose、GitHub Actions 和大多数 CI/CD 系统都使用 YAML 作为主要配置格式。
- 使用 js-yaml(JavaScript)、PyYAML/ruamel.yaml(Python)或 yq(CLI)进行编程式 JSON-YAML 转换。
试用我们的免费 JSON 转 YAML / YAML 转 JSON 工具 →
JSON 与 YAML:语法对比
JSON 和 YAML 表示相同的数据结构,但语法哲学完全不同。JSON 优先考虑机器解析,YAML 优先考虑人类可读性。
以下是相同的配置数据在两种格式中的并排对比:
{
"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 消除了大括号、方括号和大部分引号。它使用缩进表示嵌套,短横线表示数组项,冒号表示键值对。支持用 # 添加注释。
何时使用 JSON,何时使用 YAML
JSON 和 YAML 的选择取决于谁或什么来读写文件:
- 构建 REST API 和 GraphQL 响应
- 微服务间数据交换
- MongoDB 等数据库存储
- JavaScript/TypeScript 项目
- package.json、tsconfig.json 等包清单
- Kubernetes 清单文件
- Docker Compose 配置
- CI/CD 管道配置
- Ansible playbook 和基础设施即代码
- 任何需要人工频繁编辑和注释的配置文件
YAML 锚点、别名与多行字符串
YAML 提供了几个 JSON 完全没有的强大功能。
锚点与别名
YAML 锚点(&)定义可重用数据块,别名(*)引用它。合并键(<<)允许继承和覆盖:
# 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" }
# },
# ...
# }多行字符串
YAML 提供两种块标量样式来处理多行文本:
字面块(|):保持换行原样。适用于脚本、SQL 等。
折叠块(>):用空格连接行(类似 HTML)。适用于长描述。
# 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)截断指示符控制尾部换行:|+ 保留所有,|- 去除所有,|(默认)保留一个。
在 JavaScript 中转换(js-yaml)
js-yaml 是最广泛使用的 JavaScript YAML 解析器:
// 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 }]TypeScript 项目可直接使用内置类型定义。也可以使用更新的 yaml 包,支持保留注释的往返编辑。
试用我们的免费 JSON 转 YAML / YAML 转 JSON 工具 →
在 Python 中转换(PyYAML、ruamel.yaml)
Python 有两个主要 YAML 库:PyYAML 是标准选择,ruamel.yaml 是支持保留注释的现代替代方案。
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(保留注释)
ruamel.yaml 可以在修改值后保留注释完整写回:
# 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)命令行转换(yq、jq)
命令行工具适合快速转换、shell 脚本和 CI/CD 管道:
yq:YAML 瑞士军刀
yq 是轻量级便携的 YAML 命令行处理器:
# 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 处理 JSON
jq 不直接处理 YAML,但可与 yq 或 Python 结合使用:
# 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 -P快速单行命令
# 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.yamlKubernetes 清单:YAML 密集型生态
Kubernetes 使 YAML 成为云原生基础设施的通用语言。每个 Kubernetes 资源都在 YAML 中定义。
以下是典型的 Kubernetes Deployment 清单:
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: ClusterIPKubernetes 中的 YAML 模式:嵌套映射、短横线序列、多行字符串、标签/注解,以及 <code>---</code> 分隔符。
Docker Compose 与 CI/CD 配置
Docker Compose 和 GitHub Actions 等 CI/CD 平台是 YAML 的另一个主要生态:
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 工作流
GitHub Actions 使用特定的 YAML 模式如 on: 触发器和矩阵策略:
# .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.sh试用我们的免费 JSON 转 YAML / YAML 转 JSON 工具 →
YAML 陷阱:常见错误
YAML 的灵活性带来了几个臭名昭著的陷阱:
挪威问题(布尔值强制转换)
YAML 最臭名昭著的陷阱。YAML 1.1 中大量未引用值被解释为布尔值:
# 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"国家代码 NO(挪威)变成 false。解决方法:始终引用可能被误解的字符串。
缩进错误
YAML 只使用空格(不允许制表符)。务必配置编辑器使用 2 空格:
# .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: 120意外的布尔值
版本号 1.0 变成浮点数,日期 2024-01-15 可能变成日期对象。引用所有非明显字符串:
# 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 itYAML 安全:避免代码执行
YAML 解析器可能非常危险。YAML 规范包含允许实例化任意对象的标签。
危险模式(Python):yaml.load(data) 允许从 YAML 标签构造任意 Python 对象。
# 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 error安全模式:始终使用 yaml.safe_load()。
JavaScript 中,js-yaml v4 默认安全加载。Go 的标准库默认安全。
其他安全考虑:限制输入大小防止 DoS,解析后验证 schema,不要反序列化不受信任的 YAML。
功能对比表
以下是 JSON 和 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 |
常见问题
JSON 和 YAML 有什么区别?
JSON 使用大括号和双引号字符串;YAML 使用缩进。YAML 支持注释、多行字符串和锚点。JSON 解析更快且通用支持更好。YAML 更易读,适合配置文件。YAML 是 JSON 的超集。
如何在线将 JSON 转换为 YAML?
将 JSON 粘贴到在线转换工具中,工具会解析 JSON 并输出带适当缩进的 YAML。我们的免费工具支持嵌套对象、数组和大文件。
YAML 比 JSON 更适合配置文件吗?
是的,YAML 通常更适合人工频繁编辑的配置文件,因为它支持注释、多行字符串和更简洁的语法。
什么是 YAML 中的挪威问题?
挪威问题指 YAML 1.1 将国家代码 NO 解释为布尔值 false。解决方法是用引号包裹。
为什么要用 yaml.safe_load()?
yaml.load() 可以实例化任意 Python 对象,导致远程代码执行。safe_load() 限制为基本数据类型。
转换为 JSON 再转回能保留 YAML 注释吗?
不能。JSON 没有注释语法。要保留注释,使用 ruamel.yaml 或 yaml CST 解析器。
如何在命令行中转换?
使用 yq:运行 "yq -o=json file.yaml"。也可用 Python 单行命令。
转换为 JSON 时会丢失哪些 YAML 功能?
注释被丢弃,锚点被展开,多行块标量变为带 \n 的字符串,多文档变为数组,日期和二进制类型被转换。
理解 JSON 和 YAML 之间的关系对现代软件开发至关重要。JSON 主导 API 通信,YAML 统治配置世界。掌握两者之间的转换、避免常见陷阱,遵循安全最佳实践,你就能在两个生态中自如工作。