YAML(YAML Ain’t Markup Language™,YAML 不是标记语言)是一种人性化的数据序列化语言,与所有编程语言配合良好,用于日常任务,如配置文件、日志文件、跨语言数据共享和对象持久化。YAML 专注于易于人类阅读,这也是其设计目标中优先级最高的。

YAML 基于 Unicode 字符集,并围绕动态编程语言的常见本地数据类型(如映射、序列和标量)进行设计。目前的规范版本是 YAML 1.2,最新的修订版是 1.2.2(2021年10月1日)。YAML 1.2 的主要重点是使其成为 JSON 的严格超集

这份YAML语法教程将全面详细地介绍 YAML 的核心语法和结构。

一、YAML 的基本数据结构 (Primitives)

YAML 基于三种基本的数据原语来表示所有数据结构:

  1. 映射 (Mappings): 相当于哈希表或字典(如 Perl 的 hash, Python 的 dictionary)。它们是无序的键/值对集合,要求键是唯一的。
  2. 序列 (Sequences): 相当于数组或列表(如 Perl 的 array, Python 的 list)。它们是有序的节点系列。
  3. 标量 (Scalars): 相当于字符串或数字。它们是可表示为 Unicode 字符序列的不透明数据。

1.1 块样式集合 (Block Collections)

块样式(Block Styles)使用缩进来定义作用域。

映射 (Mappings)

使用冒号和空格(:)标记每个键/值对。

# 示例 2.2 映射标量到标量 (玩家统计数据)
hr: 65 # Home runs
avg: 0.278 # Batting average
rbi: 147 # Runs Batted In

序列 (Sequences)

使用短横线和空格(- )指示每个条目。

# 示例 2.1 标量序列 (棒球运动员)
- Mark McGwire
- Sammy Sosa
- Ken Griffey

嵌套结构

YAML 通过缩进处理嵌套结构。

# 示例 2.3 映射标量到序列 (各联盟的棒球俱乐部)
american:
  - Boston Red Sox
  - Detroit Tigers
  - New York Yankees
national:
  - New York Mets
  - Chicago Cubs
  - Atlanta Braves

# 示例 2.4 映射序列 (球员统计数据)
- name: Mark McGwire
  hr: 65
  avg: 0.278
- name: Sammy Sosa
  hr: 63
  avg: 0.288

注意: 缩进是 YAML 结构的关键。为保持可移植性,缩进中不得使用 Tab 字符。所有兄弟节点必须使用完全相同的缩进级别。

1.2 流样式集合 (Flow Collections)

流样式(Flow Styles)使用显式指示符(Explicit Indicators)而非缩进来指示作用域,这使其类似于 JSON。

  • 流式序列: 使用方括号 ([]) 和逗号分隔列表。
  • 流式映射: 使用花括号 ({}) 和逗号分隔。
# 示例 2.5 序列的序列
- [name, hr, avg]
- [Mark McGwire, 65, 0.278]
- [Sammy Sosa, 63, 0.288]

# 示例 2.6 映射的映射
Mark McGwire: {hr: 65, avg: 0.278}
Sammy Sosa: { hr: 63, avg: 0.288 }

二、标量 (Scalars) 的表示方法

标量内容是 YAML 中数据的实际载体。YAML 提供了多种标量样式,旨在平衡可读性和表达能力。

2.1 流式标量 (Flow Scalars)

流式标量可以跨多行,但行中断总会被折叠(折叠成空格,除非是转义的换行)。

1. 普通样式 (Plain Style)

即无引号的标量,最常用且最具可读性。

  • 限制:不能包含前导或尾随空格。
  • 限制:不能以大多数指示符开头,以避免与其他 YAML 结构产生歧义。
# 示例:普通样式
name: Mark McGwire
value: 0.278
url: https://example.com/foo#bar # 可包含冒号,只要其后不跟空格

2. 单引号样式 (Single-Quoted Style)

使用单引号 (') 包裹。如果需要表示单引号本身,则需要重复两次('')。

  • 特点:不进行转义(除了单引号本身),\ 字符没有特殊含义。
# 示例 2.17 引用标量
single: '"Howdy!" he cried.' # 内部双引号不需要转义
quoted: ' # Not a ''comment''.' # 单引号需用两个单引号表示

3. 双引号样式 (Double-Quoted Style)

使用双引号 (") 包裹。这是唯一支持转义序列的样式。

  • 支持常见的转义序列,如:\n (换行)、\t (Tab)、\b (退格)、\" (双引号)。
  • 支持 Unicode 字符转义,如 8 位 (\x41)、16 位 (\u0041)、32 位 (\U00000041)。
# 示例 2.17 引用标量
unicode: "Sosa did fine.\u263A"
control: "\b1998\t1999\t2000\n"
hex esc: "\x0d\x0a is \r\n"

2.2 块式标量 (Block Scalars)

块式标量用于处理多行文本。它们使用缩进而不是引号来包围内容。

1. 字面量样式 (Literal Style, |)

保留所有换行符(包括空行和内部缩进)。

# 示例 2.13 字面量样式:保留换行
---
|
\//||\/||
// ||
||__

# 结果:保留原始结构
# "\//||\/||\n// ||\n||__\n"

2. 折叠样式 (Folded Style, >)

换行折叠成单个空格,使得长文本易于阅读。

  • 例外:空行或缩进更多的行(more-indented lines)的换行符会被保留。
# 示例 2.14 折叠样式:换行变为空格
---
>
Mark McGwire's year was
crippled by a knee injury.

# 结果:折叠成一行
# "Mark McGwire's year was crippled by a knee injury.\n"

# 示例 2.15 折叠样式:保留空行和更多缩进的行
---
>
Sammy Sosa completed another fine season with great stats.

  63 Home Runs
  0.288 Batting Average
What a year!

3. 块标头和处理 (Header and Chomping)

块式标量可以添加一个块标头来控制缩进和尾随换行符的处理,即“Chomping”。

  • 缩进指示器 (Indentation Indicator): 可选,数字 1-9,明确指定内容缩进级别。
  • 处理指示器 (Chomping Indicator): 控制文档末尾换行符和尾随空行的处理。
    • |> (默认/Clip):保留最后的换行符,移除所有尾随空行。
    • |->- (Strip):移除最后的换行符和所有尾随空行。
    • |+>+ (Keep):保留最后的换行符和所有尾随空行。
# 示例 8.4 Chomping 处理
strip: |-
  text  
clip: |
  text  
keep: |+
  text  

三、高级结构与元数据

YAML 提供了用于数据重用、类型标记和流控制的机制。

3.1 文档流和标记

一个 YAML 字符流可以包含零个或多个文档。

  • 文档开始/指令结束标记: 三个短横线(---)用于分隔指令和文档内容,或标记文档的开始。
  • 文档结束标记: 三个点(...)指示文档结束,常用于通信通道。
  • 裸文档 (Bare Document): 不以任何指令或标记开始的文档。
# 示例 2.7 流中的两个文档
# 1998年本垒打排名
---
- Mark McGwire
- Sammy Sosa
- Ken Griffey

# 球队排名
---
- Chicago Cubs
- St Louis Cardinals

# 示例 2.8 比赛的实时消息
---
time: 20:03:20
player: Sammy Sosa
action: strike (miss)
...
---
time: 20:03:47
player: Sammy Sosa
action: grand slam
...

3.2 锚点和别名 (Anchors and Aliases)

YAML 能够表示图形结构(Directed Graph),通过锚点(Anchor)和别名(Alias)实现数据重用。

  • 锚点 (&): 用于标识(&anchor)一个节点。该节点是首次出现。
  • 别名 (*): 用于引用(*alias)先前锚定的节点。
# 示例 2.10 节点 "Sammy Sosa" 出现两次
---
hr:
  - Mark McGwire
  - &SS Sammy Sosa # 节点被标记为 SS
rbi:
  - *SS # 后续引用
  - Ken Griffey

注意: 别名节点不能指定任何属性或内容。锚点名称是一个序列化细节,不应用于传达内容信息。

3.3 标签 (Tags) 和类型系统

YAML 节点需要一个标签(tag)来指定其数据类型。标签使用感叹号(!)指示。

  • 非特定标签 (Non-Specific Tags): 未明确标记的节点会被赋予一个非特定标签,等待解析。对于非普通(non-plain)标量是 !,对于所有其他节点(包括集合和普通标量)是 ?
  • 本地标签 (Local Tags):! 开头,特定于单个应用程序。例如 !something
  • 全局标签 (Global Tags): 是 URI,全局唯一。例如 tag:yaml.org,2002:str
  • 逐字标签 (Verbatim Tags): 使用 <> 包围标签,如 !<tag:yaml.org,2002:str>

推荐的 JSON 模式类型示例

标签 (Tag Shorthand)示例描述
!!str'012345', "foo\nbar", plain stringUnicode 字符串。
!!int12345, 0o14 (八进制), 0xC (十六进制)整数。
!!float1.23015e+3, .inf (正无穷), .nan (非数字)浮点数。
!!booltrue, false, True (Core Schema)布尔值。
!!nullnull, ~, (空值)空值。
# 示例 2.23 各种显式标签
---
not-date: !!str 2002-04-28
picture: !!binary |
  R0lGODlhDAAMAIQAAP//9/X 
  17unp5WZmZgAAAOfn515eXv 
  ...  
application specific tag: !something |
  The semantics of the tag above 
  may be different for different documents.

3.4 指令 (Directives)

指令是给 YAML 处理器(Processor)的指示。指令必须出现在非缩进的行首,以百分号(%)开始。

%YAML 指令

指定文档遵循的 YAML 版本。

# 示例 6.14 “ YAML ” 指令
%YAML 1.2
---
document content

%TAG 指令

建立标签速记符号(Tag Shorthand),将句柄(Handle)与前缀(Prefix)关联起来,使标签表示更简洁。

  • 主句柄 (Primary Handle): !,默认前缀为 !(本地标签)。
  • 次句柄 (Secondary Handle): !!,默认前缀为 tag:yaml.org,2002:
  • 命名句柄 (Named Handles):!e!
# 示例 2.24 全局标签
%TAG ! tag:clarkevans.com,2002: # 定义命名句柄 !
---
!shape # 使用 ! 句柄,扩展为全局标签
- !circle
  center: &ORIGIN { x: 73, y: 129 }
  radius: 7

3.5 复杂映射键 (Complex Mapping Keys)

通常映射的键是标量。当需要使用集合(序列或映射)作为键时,必须使用问号和空格(? )来显式指示键的开始。

# 示例 2.11 序列之间的映射
? - Detroit Tigers
  - Chicago cubs
: - 2001-07-23

? [ New York Yankees, Atlanta Braves ]
: [ 2001-07-02, 2001-08-12, 2001-08-14 ]

# 示例 2.25 无序集合 (Sets)
--- !!set
? Mark McGwire
? Sammy Sosa
? Ken Griffey
# 集合被表示为键关联着空值 (null value) 的映射

四、示例总结 (Full Length Examples)

以下是 YAML 语法的完整示例,展示了前面讨论的各种元素的综合使用。

4.1 完整发票示例

此示例展示了锚点/别名、块式标量以及显式全局标签的使用。

# 示例 2.27 发票 (Invoice)
--- !<tag:clarkevans.com,2002:invoice>
invoice: 34843
date : 2001-01-23
bill-to: &id001          # 使用锚点 &id001 标记账单地址
  given : Chris
  family : Dumars
  address:
    lines: |             # 使用字面量块标量,保留换行
      458 Walkman Dr.
      Suite #292
    city : Royal Oak
    state : MI
    postal : 48046
ship-to: *id001          # 使用别名引用账单地址
product:
- sku : BL394D
  quantity : 4
  description : Basketball
  price : 450.00
- sku : BL4438H
  quantity : 1
  description : Super Hoop
  price : 2392.00
tax : 251.42
total: 4443.52
comments: Late afternoon is best. Backup contact is Nancy Billsmer @ 338-4338.

4.2 结构化日志文件示例

此示例展示了多文档流、时间戳(虽然未明确标记,但会被解析器识别)和折叠块式标量。

# 示例 2.28 日志文件 (Log File)
--- 
Time: 2001-11-23 15:01:42 -5
User: ed
Warning: This is an error message for the log file
--- 
Time: 2001-11-23 15:02:31 -5
User: ed
Warning: A slightly different error message.
--- 
Date: 2001-11-23 15:03:17 -5
User: ed
Fatal: Unknown variable "bar"
Stack:
- file: TopClass.py
  line: 23
  code: |                  # 字面量样式
    x = MoreObject("345\n")
- file: MoreClass.py
  line: 58
  code: |-                 # 字面量样式,- 剥离末尾换行
    foo = bar

YAML 语言的核心优势在于其人类可读性和与现代编程语言原生数据结构的高度匹配。通过灵活运用块样式和流样式,以及各种标量表示方法,YAML 能够优雅地序列化从简单配置到复杂图形结构的任何数据。


也可以看看