JSON と YAML は現代の開発における2大データシリアライゼーション形式です。JSON は API とマシン間通信に優れ、YAML は人が編集する設定ファイルに最適です。YAML は JSON のスーパーセットで、コメント、アンカー、複数行文字列をサポートします。YAML の落とし穴(ノルウェー問題、ブール値変換、インデントエラー)に注意してください。
- JSON は波括弧と厳密な引用を使用。YAML はインデントで最小限の記号。
- YAML はコメント(#)、アンカー/エイリアス(&/*)、複数行文字列(|と>)をサポート。
- 「ノルウェー問題」:引用なしの NO、YES、on、off は YAML 1.1 でブール値になる。
- Python では yaml.load() の代わりに常に yaml.safe_load() を使用。
- Kubernetes、Docker Compose、GitHub Actions は YAML を主要設定形式として使用。
- js-yaml(JavaScript)、PyYAML(Python)、yq(CLI)で変換。
JSON vs 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 の使い分け
選択はファイルを読み書きする主体によります:
- REST API と GraphQL レスポンス
- マイクロサービス間データ交換
- MongoDB データベース
- JavaScript/TypeScript プロジェクト
- package.json 等のマニフェスト
- Kubernetes マニフェスト
- Docker Compose 設定
- CI/CD パイプライン
- Ansible プレイブック
- 人が頻繁に編集する設定ファイル
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 は2種類のブロックスカラーを提供:
リテラルブロック(|):改行をそのまま保持。
折りたたみブロック(>):改行をスペースに置換。
# 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)チョンピング指示子:|+ 全末尾改行保持、|- 全削除、| 1つ保持。
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 プロジェクトでは組み込み型定義を使用可能。
Python での変換(PyYAML、ruamel.yaml)
Python には2つの主要 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)
CLI ツールは素早い変換に最適:
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
jq は YAML を直接扱えませんが、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 -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 マニフェスト
Kubernetes は YAML をクラウドネイティブの共通言語にしました。
典型的な 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 パターン:ネスト、シーケンス、複数行文字列、ラベル。
Docker Compose と CI/CD
Docker Compose と GitHub Actions も主要な 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 パターンを使用:
# .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.shYAML の落とし穴
YAML の柔軟性には落とし穴が伴います:
ノルウェー問題
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"国コード 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 は浮動小数点になる:
# 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.load(data) は任意のオブジェクトを生成可能。
# 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 はデフォルトで安全。
入力サイズ制限、スキーマ検証、信頼できないソースからの安全な読み込みを徹底。
機能比較表
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 はインデントベースでコメント、複数行文字列をサポート。
オンラインで JSON を YAML に変換するには?
オンラインツールに JSON を貼り付けると YAML に変換されます。
設定ファイルに YAML は JSON より良い?
はい、人が編集するファイルには YAML が適しています。
YAML のノルウェー問題とは?
YAML 1.1 が NO を false に解釈する問題。引用で解決。
なぜ yaml.safe_load() を使う?
yaml.load() は任意コード実行の危険性がある。
JSON 変換後に YAML コメントは保持される?
いいえ。ruamel.yaml を使用してください。
CLI で変換するには?
yq -o=json file.yaml を使用。
JSON 変換で失われる YAML 機能は?
コメント、アンカー、複数行ブロック、複数ドキュメント、日付型。
JSON と YAML の関係を理解することは現代のソフトウェア開発に不可欠です。