DevToolBox免费
博客

JSON 转 YAML 在线转换指南:语法、工具与最佳实践

14 分钟阅读作者 DevToolBox
TL;DR

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 优先考虑人类可读性。

以下是相同的配置数据在两种格式中的并排对比:

JSON 格式
{
  "server": {
    "host": "localhost",
    "port": 8080,
    "ssl": true
  },
  "database": {
    "name": "myapp",
    "replicas": [
      "db1.example.com",
      "db2.example.com"
    ]
  },
  "features": [
    "authentication",
    "logging",
    "rate-limiting"
  ]
}
YAML 格式
# 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-limiting

YAML 消除了大括号、方括号和大部分引号。它使用缩进表示嵌套,短横线表示数组项,冒号表示键值对。支持用 # 添加注释。

何时使用 JSON,何时使用 YAML

JSON 和 YAML 的选择取决于谁或什么来读写文件:

使用 JSON 的场景:
  • 构建 REST API 和 GraphQL 响应
  • 微服务间数据交换
  • MongoDB 等数据库存储
  • JavaScript/TypeScript 项目
  • package.json、tsconfig.json 等包清单
使用 YAML 的场景:
  • 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.yaml

jq 处理 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.yaml

Kubernetes 清单: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: ClusterIP

Kubernetes 中的 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 it

YAML 安全:避免代码执行

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 功能的全面对比:

FeatureJSONYAML
SyntaxBraces {} and brackets []Indentation-based
CommentsNot supportedSupported with #
String QuotingRequired (double quotes)Optional for most strings
Multi-line StringsEscape with \nBlock scalars: | and >
Anchors / AliasesNot supportedSupported with & and *
Multiple DocumentsOne per fileYes, separated by ---
Data TypesString, Number, Boolean, null, Array, ObjectAll JSON types + dates, binary, custom tags
Parsing SpeedFast (simple grammar)Slower (indentation-sensitive)
File SizeLarger (quotes, braces)Smaller (minimal punctuation)
ToolingUniversal (every language)Good (PyYAML, js-yaml, yq)
Primary UseAPIs, data exchangeConfiguration files
Superset RelationBase formatSuperset 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 统治配置世界。掌握两者之间的转换、避免常见陷阱,遵循安全最佳实践,你就能在两个生态中自如工作。

使用我们的免费在线工具即时转换 JSON 和 YAML。

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

Y{}JSON ↔ YAML Converter{ }JSON FormatterYMLYAML Validator & Formatter🐳Docker Compose Generator

相关文章

YAML 转 JSON 转换器完全指南:含代码示例

免费在线 YAML 转 JSON 转换器。学习 YAML 语法,在 YAML 和 JSON 之间转换,含 JavaScript、Python、Go、Bash 代码示例。

JSON vs YAML vs TOML:你应该用哪种配置格式?

比较 JSON、YAML 和 TOML 配置格式,了解语法、特性和优缺点,选择适合你项目的格式。

YAML 语法与验证:常见错误及修复方法

掌握 YAML 语法:缩进规则、常见解析错误、数据类型和配置文件最佳实践。

YAML 锚点、别名与合并键

使用锚点(&)、别名(*)和合并键(<<)掌握 YAML DRY 原则。减少 Docker Compose、CI/CD 流水线和 Kubernetes 配置中的重复。