YAML 不是标记语言(YAML™)版本 1.2

修订版 1.2.2(2021-10-01)

Copyright presently by YAML Language Development Team1
Copyright 2001-2009 by Oren Ben-Kiki, Clark Evans, Ingy döt Net

此文档可自由复制,前提是不作修改。

此文档的状态

这是 YAML 规范 v1.2.2。 它定义了 YAML 1.2 数据语言。 与 YAML 规范 v1.2 相比没有规范性变更。 此修订版的主要目标是纠正错误并增加清晰度。

此修订版还致力于使 YAML 语言开发过程更加 开放、更加透明,并更容易让人们参与贡献。 输入格式现在是 Markdown,而不是 DocBook,并且图像由 纯文本 LaTeX 文件生成,而不是由专有绘图软件生成。 该规范的所有源内容都公开托管2

上一版 YAML 规范3 发布于 12 年前。 在这段时间里,YAML 的流行度显著增长。 改进该语言并使其发展以满足用户需求和 期望的工作仍在进行中。 虽然此规范修订版没有对 YAML 作出实际更改,但它 开启了一个过程,使该语言能够演进并保持现代化。

YAML 规范常常被认为对于一个看起来如此简单的东西而言 过于复杂。 即使 YAML 经常用于软件配置,它一直都是 并将继续是一个完整的数据序列化语言。 YAML 未来的计划侧重于使该语言和生态系统更 强大、更可靠,同时为实现者简化开发过程。

虽然此规范修订版仅限于信息性 更改,但有配套文档旨在指导 YAML 框架 实现者和 YAML 语言用户。 这些文档可以在此规范的已发布修订版之间持续演进和扩展。

参见:

摘要

YAML™(与 “camel” 押韵)是一种对人类友好、跨语言、基于 Unicode 的 数据序列化语言,围绕动态编程语言常见的原生数据类型 设计。 它广泛适用于从配置文件到 互联网消息传递、对象持久化、数据审计和可视化等编程需求。 与字符的 Unicode 标准4 一起,此规范 提供了理解 YAML 版本 1.2 以及 创建处理 YAML 信息的程序所需的全部信息。

目录

第 1 章 YAML 简介

YAML(“YAML Ain’t Markup Language”的递归首字母缩略词)是一种数据 序列化语言,旨在对人类友好,并能很好地配合现代 编程语言处理常见的日常任务。 此规范既是 YAML 语言的介绍,也是对 支持它的概念的介绍。 它也是开发用于处理 YAML 的 应用程序所需信息的完整规范。

开放、可互操作且易于理解的工具极大地推动了计算的发展。 YAML 从一开始就被设计为对处理数据的人有用且友好。 它使用 Unicode 可打印字符,其中一些字符提供结构 信息,其余字符则包含数据本身。 YAML 通过尽量减少结构字符的数量,并允许数据以自然且有意义的 方式呈现自身,从而实现了一种独特的简洁性。 例如,缩进可用于表示结构,冒号分隔 键/值对,而短横线用于 创建“项目符号”列表

有许多种数据结构,但它们都可以用三种基本原语充分 表示映射(哈希/字典)、 序列(数组/列表)和标量 (字符串/数字)。 YAML 利用这些原语,并添加一个简单的类型系统和别名 机制,以形成一种完整的语言,用于序列化任何原生数据 结构。 虽然大多数编程语言都可以使用 YAML 进行数据序列化,但 YAML 在与那些从根本上围绕三种基本原语构建的语言配合使用时表现尤为出色。 这些语言包括 JavaScript、Perl、PHP、Python 和 Ruby 等常见动态语言。

用于编程的语言有数百种,但用于存储和传输数据的语言却只有少数几种。 尽管其潜力几乎没有边界,YAML 仍是专门为很好地服务于常见用例而创建的,例如: 配置文件、日志文件、进程间消息传递、跨语言数据共享、对象持久化以及 复杂数据结构调试。 当数据易于查看和理解时,编程就会成为一项更简单的任务。

1.1. 目标

YAML 的设计目标按优先级递减排列如下:

  1. YAML 应当易于人类阅读。
  2. YAML 数据应当可在编程语言之间移植。
  3. YAML 应当匹配动态语言的原生数据 结构
  4. YAML 应当具有一致的模型,以支持通用工具。
  5. YAML 应当支持单遍处理。
  6. YAML 应当具有表达能力且可扩展。
  7. YAML 应当易于实现和使用。

1.2. YAML 历史

YAML 1.0 规范由 Clark Evans、Oren Ben-Kiki 和 Ingy döt Net 在通过 yaml-core 邮件列表5进行了 3 年协作设计工作后,于 2004 年初发布。 该项目最初植根于 Clark 和 Oren 在 SML-DEV6 邮件列表上的工作(用于简化 XML),以及 Ingy 为 Perl 编写的纯文本 序列化模块7。 该语言从许多先于它出现的其他技术和格式中获得了大量灵感。

第一个 YAML 框架于 2001 年用 Perl 编写,而 Ruby 是第一个 在 2003 年将 YAML 框架作为其核心语言发行版一部分发布的 语言。

YAML 1.18 规范于 2005 年发布。 大约在这一时期,开发者开始注意到 JSON9。 纯属巧合的是,JSON 几乎是 YAML 的一个完整子集(在语法和语义上都是如此)。

2006 年,Kyrylo Simonov 开发了 PyYAML10 和 LibYAML11。 各种编程语言中的许多 YAML 框架都构建在 LibYAML 之上, 还有许多框架将 PyYAML 视为其实现的可靠参考。

YAML 1.23 规范于 2009 年发布。 它的主要重点是使 YAML 成为 JSON 的严格超集。 它还移除了许多存在问题的隐式类型建议。

自 1.2 规范发布以来,YAML 的采用继续增长, 许多大型项目将其用作主要接口语言。 2020 年,新的 YAML 语言设计团队开始定期会面, 讨论对 YAML 语言和规范的改进;以更好地满足 用户和用例的需求与期望。

此 YAML 1.2.2 规范于 2021 年 10 月发布,是 YAML 重启发展旅程的第一步。 YAML 现在比以往任何时候都更受欢迎,但要发挥其全部潜力, 仍有一长串问题需要解决。 YAML 设计团队专注于让 YAML 尽可能变得更好。

1.3. 术语

本文档中的关键词 “MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、 “SHOULD NOT”、“RECOMMENDED”、“MAY” 和 “OPTIONAL” 应按 RFC 211912 中所述进行 解释。

本文档其余部分安排如下。 第 2 章简要预览 YAML 的主要特性。 第 3 章描述 YAML 信息模型以及 在该模型和 YAML 文本格式之间相互转换的过程。 文档主体,即第 456789 章,正式 定义此文本格式。 最后,第 10 章推荐基本 YAML 模式。

第 2 章 语言概览

本节快速展示 YAML 的表达能力。 不期望初次阅读者理解所有示例。 相反,这些选例被用作规范其余部分的 动机。

2.1. 集合

YAML 的块集合使用缩进表示作用域,并让每个条目从 独立的一行开始。 块序列用一个短横线和空格(“- ”)表示每个条目。 映射使用冒号和空格(“: ”)标记每个键/值对注释以 octothorpe(也称为 “hash”、“sharp”、 “pound” 或 “number sign” - “#”)开头。

示例 2.1 标量序列(棒球运动员)

- Mark McGwire
- Sammy Sosa
- Ken Griffey

示例 2.2 标量到标量的映射(球员统计)

hr:  65    # Home runs
avg: 0.278 # Batting average
rbi: 147   # Runs Batted In

示例 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 还有流式风格,使用显式指示符而不是 缩进来表示作用域。 流式序列写作逗号分隔的列表,并置于 括号内。 类似地,流式映射使用括号

示例 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,
 }

2.2. 结构

YAML 使用三个短横线(“---”)将 指令文档 内容分隔开。 如果不存在指令, 这也用于表示文档的开始。 三个点( “...”)表示一个文档结束, 但不开始新的文档,用于通信通道。

示例 2.7 流中的两个文档(每个都有前导注释)

# Ranking of 1998 home runs
---
- Mark McGwire
- Sammy Sosa
- Ken Griffey

# Team ranking
---
- Chicago Cubs
- St Louis Cardinals

示例 2.8 来自比赛的逐球播报 Feed

---
time: 20:03:20
player: Sammy Sosa
action: strike (miss)
...
---
time: 20:03:47
player: Sammy Sosa
action: grand slam
...

重复的节点(对象)首先通过锚点标识 (用和号 - “&” 标记),随后再被 别名化(用星号引用 - “*”)。

示例 2.9 带有两个注释的单个文档

---
hr: # 1998 hr ranking
- Mark McGwire
- Sammy Sosa
# 1998 rbi ranking
rbi:
- Sammy Sosa
- Ken Griffey

示例 2.10 “Sammy Sosa” 的节点在 此文档中出现两次

---
hr:
- Mark McGwire
# Following node labeled SS
- &SS Sammy Sosa
rbi:
- *SS # Subsequent occurrence
- Ken Griffey

问号和空格(“? ”) 表示复杂映射。 在块集合内,键/值 对可以紧随 短横线冒号问号之后开始。

示例 2.11 序列之间的映射

? - Detroit Tigers
  - Chicago cubs
: - 2001-07-23

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

示例 2.12 紧凑嵌套映射

---
# Products purchased
- item    : Super Hoop
  quantity: 1
- item    : Basketball
  quantity: 4
- item    : Big Shoes
  quantity: 1

2.3. 标量

标量内容可以用表示法编写, 使用字面样式 (由“|”指示),其中所有换行都具有意义。 或者,也可以使用折叠样式(由 “>”表示), 其中每个换行都会被折叠 为一个空格,除非它结束一个行或一个 缩进更多的行。

示例 2.13 在字面量中,换行会被保留

# ASCII Art
--- |
  \//||\/||
  // ||  ||__

示例 2.14 在折叠标量中,换行会变为空格

--- >
  Mark McGwire's
  year was crippled
  by a knee injury.

示例 2.15 对于“缩进更多”的行和空白 行,折叠换行会被保留

--- >
 Sammy Sosa completed another
 fine season with great stats.

   63 Home Runs
   0.288 Batting Average

 What a year!

示例 2.16 缩进决定作用域

name: Mark McGwire
accomplishment: >
  Mark set a major league
  home run record in 1998.
stats: |
  65 Home Runs
  0.278 Batting Average

YAML 的流式标量包括普通 样式(目前为止的大多数示例)和 两种引号样式。 双引号样式提供转义序列。 当不需要转义时,单引号样式很有用。 所有流式标量都可以跨多行;换行总是会被折叠

示例 2.17 带引号的标量

unicode: "Sosa did fine.\u263A"
control: "\b1998\t1999\t2000\n"
hex esc: "\x0d\x0a is \r\n"

single: '"Howdy!" he cried.'
quoted: ' # Not a ''comment''.'
tie-fighter: '|\-*-/|'

示例 2.18 多行流式标量

plain:
  This unquoted scalar
  spans many lines.

quoted: "So does this
  quoted scalar.\n"

2.4. 标签

在 YAML 中,未标记节点会根据应用程序被赋予一种类型。 此规范中的示例通常使用来自故障安全模式seqmapstr 类型。 少数示例也使用来自JSON 模式intfloatnull 类型。

示例 2.19 整数

canonical: 12345
decimal: +12345
octal: 0o14
hexadecimal: 0xC

示例 2.20 浮点数

canonical: 1.23015e+3
exponential: 12.3015e+02
fixed: 1230.15
negative infinity: -.inf
not a number: .nan

示例 2.21 其他

null:
booleans: [ true, false ]
string: '012345'

示例 2.22 时间戳

canonical: 2001-12-15T02:59:43.1Z
iso8601: 2001-12-14t21:59:43.10-05:00
spaced: 2001-12-14 21:59:43.10 -5
date: 2002-12-14

显式类型化使用标签表示,并使用感叹号(“!”) 符号。 全局标签是 URI,并且可以在标签 简写表示法中使用 句柄指定。 也可以使用特定于应用程序本地标签

示例 2.23 各种显式标签

---
not-date: !!str 2002-04-28

picture: !!binary |
 R0lGODlhDAAMAIQAAP//9/X
 17unp5WZmZgAAAOfn515eXv
 Pz7Y6OjuDg4J+fn5OTk6enp
 56enmleECcgggoBADs=

application specific tag: !something |
 The semantics of the tag
 above may be different for
 different documents.

示例 2.24 全局标签

%TAG ! tag:clarkevans.com,2002:
--- !shape
  # Use the ! handle for presenting
  # tag:clarkevans.com,2002:circle
- !circle
  center: &ORIGIN {x: 73, y: 129}
  radius: 7
- !line
  start: *ORIGIN
  finish: { x: 89, y: 102 }
- !label
  start: *ORIGIN
  color: 0xFFEEBB
  text: Pretty vector drawing.

示例 2.25 无序集合

# Sets are represented as a
# Mapping where each key is
# associated with a null value
--- !!set
? Mark McGwire
? Sammy Sosa
? Ken Griffey

示例 2.26 有序映射

# Ordered maps are represented as
# A sequence of mappings, with
# each mapping having one key
--- !!omap
- Mark McGwire: 65
- Sammy Sosa: 63
- Ken Griffey: 58

2.5. 完整示例

下面是两个完整的 YAML 示例。 第一个是发票示例;第二个是日志文件示例。

示例 2.27 发票

--- !<tag:clarkevans.com,2002:invoice>
invoice: 34843
date   : 2001-01-23
bill-to: &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.

示例 2.28 日志文件

---
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

第 3 章 过程和模型

YAML 既是一种文本格式,也是一种用于在此格式中 呈现任何原生数据 结构的方法。 因此,此规范定义了两个概念:一类称为 YAML 表示的数据对象, 以及一种用于将 YAML 表示呈现为一系列字符的语法,称为 YAML

YAML 处理器是一种在这些互补视图之间转换信息的工具。 假定 YAML 处理器代表另一个模块执行其工作, 该模块称为应用程序。 本章描述 YAML 处理器必须向应用程序提供或从应用程序获取的 信息结构。

YAML 信息以两种方式使用:供机器处理,以及供人类 使用。 调和这两种视角的挑战最好通过三个 不同的转换阶段来完成:表示序列化呈现表示处理 YAML 如何看待原生数据结构以实现 编程环境之间的可移植性。 序列化关注的是将 YAML 表示转换为 序列形式,也就是具有顺序访问约束的形式。 呈现处理如何以对人类友好的方式,将 YAML 序列化格式化为一系列 字符。

3.1. 过程

原生数据结构和字符 之间的转换 会在若干逻辑上独立的阶段中完成,每个阶段都有明确定义的输入和输出 数据模型,如下图所示:

图 3.1. 处理概览

处理概览

YAML 处理器不需要暴露序列化表示 阶段。 它可以直接在原生数据 结构和字符 之间转换(上图中的转储加载)。 然而,这种直接转换应当以这样的方式进行:使原生数据 结构仅从 表示中可用的信息构造出来。 特别是,映射键顺序注释标签句柄构造 期间不应被引用。

3.1.1. 转储

将原生数据结构转储为字符使用 以下三个阶段完成:

表示原生数据结构

YAML 使用三种节点种类表示任何原生数据结构序列 - 有序的一系列条目;映射 - 唯一的无序关联; 以及标量 - 任何具有不透明结构、 可呈现为一系列 Unicode 字符的数据。

组合起来,这些原语生成有向图结构。 选择这些原语是因为它们既强大又熟悉: 序列对应 Perl 数组和 Python 列表,映射 对应 Perl 哈希表和 Python 字典。 标量表示字符串、整数、日期和其他原子 数据类型。

每个 YAML 节点除了其种类内容之外,还需要一个标签 来指定其数据类型。 类型说明符要么是全局 URI,要么是作用域限于单个 应用程序本地说明符。 例如,整数在 YAML 中表示为一个标量加上全局 标签tag:yaml.org,2002:int”。 同样,某个特定组织使用的发票对象,可以表示为一个映射, 并带有本地 标签!invoice”。 这个简单模型可以独立于编程语言表示任何数据结构。

序列化表示图

对于顺序访问媒介,例如事件回调 API,YAML 表示必须被序列化为一棵 有序树。 由于在 YAML 表示中,映射键是无序的,并且节点可能 被引用多次(有多个传入“箭头”),因此 序列化过程需要对映射键施加排序, 并将对某个给定节点的第二次及后续引用 替换为称为别名的 占位符。 YAML 不规定这些序列化细节如何选择。 由 YAML 处理器负责生成 对人类友好的键顺序锚点名称,可能还会借助应用程序的帮助。 此过程的结果,即 YAML 序列化树, 随后可以被遍历,以产生一系列事件调用,用于对 YAML 数据进行单遍处理。

呈现序列化树

最终输出过程是以对人类友好的方式,将 YAML 序列化呈现为 字符。 为了最大化人类可读性,YAML 提供了丰富的风格选项, 远超简单数据存储的最低功能需求。 因此,YAML 处理器在创建时, 需要引入各种呈现 细节,例如 节点样式的选择、如何 格式化标量内容缩进量、要使用哪些标签 句柄、 哪些节点标签保留为未指定、要提供的指令集合, 甚至可能包括要添加哪些注释。 虽然其中一部分可以借助应用程序完成,但一般而言, 此过程应当由用户的偏好来引导。

3.1.2. 加载

从字符加载原生数据结构 使用 以下三个阶段完成:

解析呈现流

解析呈现的逆过程,它接收一个由 字符组成的,并生成一棵序列化树。 解析会丢弃在呈现过程中引入的所有细节, 只报告序列化树。 解析可能由于格式不良输入而失败。

组成表示图

组成接收一棵序列化树,并 生成一个表示图。 组成会丢弃在序列化过程中引入的所有细节, 只生成表示图。 组成可能因多种原因失败,详见下文

构造原生数据结构

最终输入过程是从 YAML 表示构造原生数据结构。 构造必须只基于表示中可用的信息, 而不能基于其他序列化呈现 细节,例如注释指令映射键顺序节点样式标量内容格式缩进级别等。 构造可能由于所需原生数据 类型不可用而失败。

3.2. 信息模型

本节规定上述过程结果的形式化细节。 为了最大化编程语言和实现之间的数据可移植性, YAML 用户应当注意序列化呈现属性与属于 YAML 表示的属性之间的区别。 因此,虽然为了将 YAML 表示扁平化为顺序访问媒介, 有必要对映射 键施加顺序,但这种序列化 细节不得用于传递应用程序级别的信息。 类似地,虽然缩进技术和节点 样式的选择是人类可读性所需的,但这些呈现细节 既不是 YAML 序列化的一部分,也不是 YAML 表示的一部分。 通过仔细分离序列化呈现所需的属性,YAML 表示应用程序信息将在 各种编程环境之间保持一致且可移植。

下图总结了三个信息模型。 实心箭头表示组合,空心箭头表示继承,“1”和 “*”表示“一”和“多” 关系。 单个“+”表示序列化细节,双“++”表示 呈现细节。

图 3.2. 信息模型

信息模型

3.2.1. 表示图

YAML 对原生数据 结构表示是一个有根、连通的 带标签节点有向图。 所谓“有向图”,我们指一组节点和有向边(“箭头”), 其中每条边将一个节点连接到另一个节点(参见形式化有向图 定义13)。 所有节点都必须能够通过这些边从根节点到达。 注意,YAML 图可以包含环,并且一个节点可能有多个 传入边。

以其他节点定义的节点集合;不依赖任何其他节点节点标量。 YAML 支持两种种类集合节点序列映射映射节点有些棘手,因为它们的 是无序的,并且必须 唯一

图 3.3. 表示模型

表示模型

3.2.1.1. 节点

YAML 节点表示单个原生数据结构。 此类节点具有三种种类之一的内容:标量、序列或映射。 此外,每个节点都有一个标签,用于限制内容可能具有的 值集合。

标量

标量节点的内容是一个不透明的数据项,可以被呈现为 零个或多个 Unicode 字符的序列。

序列

序列节点的内容是零个或多个节点的有序序列。 特别地,一个序列可以多次包含同一个节点。 它甚至可以包含它自身。

映射

映射节点的内容是一组无序的键/值节点 ,并带有限制:每个键都必须是唯一的。 YAML 对节点不施加进一步限制。 特别是,键可以是任意节点,同一个节点可以用作多个键/值对的 值,而映射甚至可以包含它自身作为 键或值。

3.2.1.2. 标签

YAML 使用一个简单标识符(称为标签)来表示 原生数据结构的类型信息。 全局标签是 URI,因此在所有应用程序之间都是全局唯一的。 推荐所有全局 YAML 标签使用“tag:” URI 方案14。 相比之下,本地标签特定于单个应用程序。 本地标签以“!”开头, 不是 URI,也不期望全局 唯一。 YAML 提供“TAG”指令, 使标签表示法不那么冗长;它还 提供从本地标签迁移到全局标签的简便方式。 为确保这一点,本地标签被限制为 URI 字符集,并使用 URI 字符转义

YAML 不强制要求以相同子串开头的不同标签之间存在任何特殊关系。 以 URI 片段结尾(包含“#”)的标签也不例外;共享 同一基础 URI 但片段部分不同的标签被视为 不同的独立标签。 按约定,片段用于标识标签的不同“变体”, 而“/”用于定义嵌套标签 “命名空间”层级。 然而,这只是一个约定,每个标签都可以采用自己的规则。 例如,Perl 标签可以使用“::”来 表示命名空间层级,Java 标签可以使用“.”等。

YAML 标签用于将元信息与每个节点关联起来。 特别是,每个标签必须指定预期的节点种类标量序列映射)。 标量标签还必须提供一种机制,用于将格式化内容 转换为规范形式,以支持相等性测试。 此外,标签可以提供其他信息,例如用于验证的 允许内容值集合、用于标签解析的机制,或 适用于该标签所有节点的任何其他数据。

3.2.1.3. 节点比较

由于 YAML 映射要求唯一, 表示必须包含 一种用于测试节点相等性的机制。 这并非易事,因为 YAML 允许以多种方式格式化标量 内容。 例如,整数十一可以写作“0o13”(八进制)或“0xB” (十六进制)。 如果这两种记法都用作同一个映射中的,只有能够识别整数格式的 YAML 处理器才能正确地将 重复标记为错误。

规范形式

YAML 通过要求每个 标量标签都必须指定一种机制, 用于从任何 格式化内容生成规范 形式,来支持标量相等性的需求。 此形式是一个 Unicode 字符串,它同样呈现相同的 内容,并可用于相等性测试。

相等性

两个节点必须具有相同的标签内容才是相等的。 由于每个标签正好适用于一种种类, 这意味着两个 节点必须具有相同的种类才相等。

两个标量只有在它们的标签和 规范形式逐字符相等时才相等。 集合的相等性以递归方式定义。

两个序列只有在它们具有相同的标签和长度,并且 一个序列中的每个节点都等于另一个 序列中对应的节点时才相等。

两个映射只有在它们具有相同的标签、相等的 集合,并且此集合中的每个在两个 映射中都关联到相等的时才相等。

不同的 URI 方案可以为测试 URI 的相等性定义不同规则。 由于不能合理地期望 YAML 处理器 了解所有这些规则, 它必须退而使用对标签进行简单逐字符比较的方式, 以确保一致性。 这也恰好是“tag:” URI 方案定义的比较方法。 因此,YAML 流中的标签必须以规范方式呈现, 以便这种比较能够产生正确结果。

如果一个节点通过别名将其自身作为后代,那么确定该节点的 相等性是实现定义的。

YAML 处理器可以将相等的标量视为同一对象。

唯一性

如果一个映射中没有 两个键彼此相等,则这些键是唯一的。 显然,相同的节点始终被认为相等。

3.2.2. 序列化树

为了使用串行 API 表达 YAML 表示, 有必要对映射键施加顺序, 并使用别名节点来表示先前遇到的节点的 后续出现。 此过程的结果是一棵序列化树,其中每个节点都有 一组有序子节点。 可以遍历此树以用于串行事件式 API。 从串行接口构造原生数据结构时, 不应使用键顺序锚点 名称来保存应用程序数据。

图 3.4. 序列化模型

序列化模型

3.2.2.1. 映射键顺序

表示模型中,映射 键没有顺序。 要序列化一个映射,必须对其 施加排序。 此顺序是序列化细节, 在组成 表示图时不应使用它(因而也不应用于保存 应用程序 数据)。 在每一种节点顺序有意义的情况下,都必须使用序列。 例如,有序映射可以表示序列,其中包含多个 映射,每个映射都是一个单独的键/值对。 YAML 为这种情况提供了方便的紧凑表示法

3.2.2.2. 锚点和别名

表示图中,一个节点可以 出现在多个 集合中。 当序列化此类数据时,节点的第一次 出现会由一个锚点标识。 每次后续出现都会被序列化为一个别名节点, 该别名节点引用回 此锚点。 否则,锚点名称是序列化 细节,并在 组成完成后被丢弃。 当从序列化事件组成一个 表示图时,别名 事件引用序列化中最近的、带有 指定锚点的事件。 因此,锚点在序列化中不需要唯一。 此外,锚点也不需要有引用它的别名节点。

3.2.3. 呈现流

YAML 呈现是一个使用 Unicode 字符的, 它利用 样式标量内容格式注释指令和其他 呈现细节,以一种人类可读的 方式呈现 YAML 序列化。 YAML 允许多个序列化树包含在同一个 YAML 呈现流中,作为一系列由标记分隔的文档

图 3.5. 呈现模型

呈现模型

3.2.3.1. 节点样式

每个节点都以某种样式呈现,具体取决于其种类。 节点样式是呈现细节,不会反映在 序列化树表示图中。 样式分为两组。 块式样式使用缩进表示结构。 相比之下,流式样式依赖显式指示符

YAML 提供了一组丰富的标量样式块标量样式包括字面样式折叠样式流式标量样式包括普通 样式和两种引号样式: 单引号样式双引号样式。 这些样式在表达能力和可读性之间提供了一系列权衡。

通常,块序列映射 从下一行开始。 在某些情况下,YAML 也允许嵌套集合在行内开始, 以获得更紧凑的表示法。 此外,YAML 为嵌套在流式 序列中的、带有 单个键/值对流式映射 提供了一种紧凑 表示法。 这些允许一种自然的“有序映射”表示法。

图 3.6. 种类/样式组合

种类/样式组合

3.2.3.2. 标量格式

YAML 允许标量以几种格式呈现。 例如,整数“11”也可以 写作“0xB”。 标签必须指定一种机制,用于将格式化内容转换为 规范形式,以便用于相等性 测试。 与节点样式一样,格式也是呈现细节,不会反映 在序列化树表示图中。

3.2.3.3. 注释

注释是一种呈现 细节,不得对 序列化树表示图产生任何影响。 特别是,注释不会与特定节点关联。 注释的通常目的是在文件的人类维护者之间进行沟通。 一个典型示例是配置文件中的注释。 注释不得出现在标量内部,但可以与集合中的此类 标量交错出现。

3.2.3.4. 指令

每个文档都可以关联一组指令。 指令具有名称和一个可选的参数序列。 指令是给 YAML 处理器的指示,并且像所有其他 呈现细节一样,不会反映在 YAML 序列化树表示图中。 此版本的 YAML 定义了两个指令:“YAML”和“TAG”。 所有其他指令都为 YAML 的未来版本保留

3.3. 加载失败点

从 YAML 加载原生 数据结构的过程具有 若干潜在的失败点。 字符可能格式不良别名可能未标识未指定标签可能无法解析标签可能无法识别内容可能无效映射可能不唯一,并且原生 类型可能不可用。 这些失败都会导致加载不完整。

部分表示不需要解析每个节点标签,并且格式化标量 内容规范形式也不需要可用。 这种较弱的表示适用于对文档中所使用类型的知识 不完整的情况。

相比之下,完整表示会指定每个节点标签,并 提供格式化 标量内容规范形式,从而允许进行 相等性测试。 为了构造原生数据 结构,完整表示是必需的。

图 3.7. 加载失败点

加载失败点

3.3.1. 格式良好的流和已标识 别名

格式良好的字符必须匹配 后续各章中指定的 BNF 产生式。 成功加载还要求每个别名都应引用一个先前 由锚点标识节点。 YAML 处理器应当拒绝格式不良的流未标识 别名。 YAML 处理器可以从语法错误中恢复,例如通过 忽略输入的某些 部分,但它必须提供报告此类错误的机制。

3.3.2. 已解析标签

通常,大多数标签不会在字符中显式指定。 在解析期间,缺少显式标签节点 会被赋予一个非特定 标签:对于非普通标量是“!”, 对于所有其他节点是“?”。 组成完整表示要求每个这样的非特定 标签都 被解析特定标签,无论它是全局标签还是本地标签

解析节点标签必须只依赖于 以下三个 参数:(1) 节点的非特定标签,(2) 从 到该节点的路径,以及 (3) 该 节点内容(因而还有种类)。 当一个节点有多次出现(使用别名)时,标签解析 必须只依赖于到第一次(带锚点) 出现的路径。

注意,解析不得考虑呈现 细节,例如 注释缩进节点样式。 此外,解析不得考虑任何其他节点内容,除了 从到被解析节点的路径上直接键节点内容。 最后,解析不得考虑集合中兄弟节点内容,也不得考虑与正在解析的键节点 关联的值节点内容

这些规则确保标签解析可以在中首次遇到节点时 立即执行,通常是在其内容解析之前。 此外,标签解析只需要引用数量相对较少的 先前已解析节点。 因此,在大多数情况下,单遍处理器中的标签解析 既是可能的,也是实用的。

YAML 处理器应当根据节点种类, 将具有“!”非特定标签的 节点解析为 “tag:yaml.org,2002:seq”、“tag:yaml.org,2002:map”或 “tag:yaml.org,2002:str”。 这种标签解析约定允许 YAML 字符的作者 有效地“禁用”标签解析过程。 通过显式指定“!” 非特定标签属性节点随后会 根据其种类被解析为“普通”序列映射 或字符串。

特定于应用程序的标签解析规则应当限于解析 “?”非特定标签,最常见的是解析普通标量。 这些标量可以与一组正则表达式匹配,以提供整数、浮点数、时间戳和类似类型的 自动解析。 应用程序也可以将映射节点内容与 预期集合匹配,以自动解析点、复数和类似 类型。 解析后的序列节点类型(例如“有序映射”)也是 可能的。

也就是说,标签解析特定于应用程序。 因此,YAML 处理器应当提供一种机制, 允许 应用程序覆盖并扩展这些默认标签 解析规则。

如果文档包含未解析标签,YAML 处理器就无法 组成完整表示图。 在这种情况下,YAML 处理器可以基于每个节点的种类并允许使用非特定标签, 组成部分表示

3.3.3. 已识别且有效的标签

要成为有效节点,一个节点必须具有被 YAML 处理器识别标签, 并且其内容必须满足此 标签施加的约束。 如果文档包含带有未识别标签无效 内容标量节点,则只能组成部分表示。 相比之下,YAML 处理器始终可以为未识别或无效的集合 组成完整 表示,因为 集合相等性并不依赖于对集合 数据类型的了解。 然而,这样的完整表示不能用于 构造 原生数据结构

3.3.4. 可用标签

在给定处理环境中,不一定存在与给定标签 对应的可用原生类型。 如果一个节点的标签不可用,YAML 处理器将无法为其 构造原生数据结构。 在这种情况下,仍然可以组成完整表示,并且 应用程序可能希望直接使用此表示

第 4 章 语法约定

以下各章使用带参数的 BNF 产生式,正式定义 YAML 字符的语法。 每个 BNF 产生式都有名称和编号,便于引用。 在可能的情况下,基本结构会先于使用它们的更复杂 结构以“自底向上”的方式指定。

产生式附带示例,并以双栏并排格式呈现。 左侧是 YAML 示例,右侧是该示例的另一种 YAML 视图。 右侧视图在可能时使用 JSON。 否则,它会使用尽可能接近 JSON 的 YAML 形式。

4.1. 产生式语法

产生式使用语法 production-name ::= term 定义,其中 term 可以是:

原子项
  • 带引号的字符串("abc"), 它匹配该字符连接。单个 字符通常用单引号书写('a')。
  • 十六进制数(x0A),它匹配该 Unicode 码位处的字符。
  • 十六进制数范围([x20-x7E]),它匹配 Unicode 码位位于该范围内的 任意字符。
  • 产生式名称(c-printable),它匹配 该产生式。
环视
  • [ lookahead = term ], 如果 term 会匹配,则匹配空字符串。
  • [ lookahead ≠ term ], 如果 term 不会 匹配,则匹配空字符串。
  • [ lookbehind = term ], 如果 term 会从本行先前任意位置开始并在当前位置结束而匹配, 则匹配空字符串。
特殊产生式
  • <start-of-line>, 它匹配行首处的空字符串。
  • <end-of-input>, 匹配输入末尾处的空字符串。
  • <empty>,它 (总是)匹配空字符串。
带括号项

匹配其内容。

连接

term-one term-two,它 匹配 term-one 后跟 term-two

选择

term-one | term-two,它 在可能时匹配 term-one, 否则匹配 term-two

量化项:
  • term?,它匹配 (term | <empty>)
  • term*,它匹配 (term term* | <empty>)
  • term+,它匹配 (term term*)

注:量化项总是贪婪的。

优先级顺序是括号化,然后是量化,然后是 连接,然后是选择。

产生式定义中的某些行可能带有如下形式的注释:

production-a ::=
  production-b      # clarifying comment

这些注释仅用于提供信息。 例如,一个写着 # not followed by non-ws char 的注释只是表示 你应当注意,实际产生式规则会按所描述的方式运行, 即使仅从该特定产生式本身的内容来看并不明显。

4.2. 产生式参数

某些产生式在名称后带有括号中的参数,例如 s-line-prefix(n,c)。 带参数的产生式是一组(无限)产生式的简写, 其中每个参数都有一个固定值。

例如,此产生式:

production-a(n) ::= production-b(n)

是以下内容的简写:

production-a(0) ::= production-b(0)
production-a(1) ::= production-b(1)
…

而此产生式:

production-a(n) ::=
  ( production-b(n+m) production-c(n+m) )+

是以下内容的简写:

production-a(0) ::=
    ( production-b(0) production-c(0) )+
  | ( production-b(1) production-c(1) )+
  | …
production-a(1) ::=
    ( production-b(1) production-c(1) )+
  | ( production-b(2) production-c(2) )+
  | …
…

参数如下:

缩进:nm

可以是任何自然数,包括零。n 也可以是 -1。

上下文:c

此参数允许产生式根据其 周围环境调整行为。 YAML 支持两组上下文,区分块式样式流式样式

可以是以下任意值:

  • BLOCK-IN – 块 上下文内部
  • BLOCK-OUT – 块 上下文外部
  • BLOCK-KEY – 块 键上下文内部
  • FLOW-IN – 流式 上下文内部
  • FLOW-OUT – 流式 上下文外部
  • FLOW-KEY – 流式键 上下文内部
(块)截断:t

流式标量的换行截断行为。 可以是以下任意值:

  • STRIP – 移除所有尾随 换行
  • CLIP – 移除除第一个之外的所有尾随 换行
  • KEEP – 保留所有尾随 换行

4.3. 产生式命名约定

为了更容易跟随产生式组合,产生式名称使用 前缀式命名约定。 每个产生式都会根据其开始和 结束字符的类型获得一个前缀。

e-

匹配无字符的产生式。

c-

以特殊字符开始并结束的产生式。

b-

匹配单个换行的产生式。

nb-

以非断行 字符开始并结束的产生式。

s-

空白 字符开始并结束的产生式。

ns-

以非空格 字符开始并结束的产生式。

l-

匹配完整行的产生式。

X-Y-

X- 字符开始并以 Y- 字符结束的产生式, 其中 X-Y- 是上述任一 前缀。

X+, X-Y+

如上所述的产生式,但具有额外属性:匹配内容的 缩进级别大于指定的 n 参数。

第 5 章 字符产生式

5.1. 字符集

为确保可读性,YAML 只使用 Unicode 字符集的 可打印子集。 允许的字符范围明确排除了 C0 控制块15 x00-x1F(允许的 TAB x09、LF x0A 和 CR x0D 除外)、DEL x7F、C1 控制块 x80-x9F(允许的 NEL x85 除外)、 代理项块16 xD800-xDFFFxFFFExFFFF

在输入时,YAML 处理器必须接受此 可打印 子集中的所有字符。

在输出时,YAML 处理器必须只生成此 可打印子集中的字符。 此集合之外的字符必须使用转义序列 呈现。 此外,任何已知为不可打印的允许字符也应当被 转义

注:这不是强制性的,因为完整实现需要 大量字符属性表。

[1] c-printable ::=
                         # 8 bit
    x09                  # Tab (\t)
  | x0A                  # Line feed (LF \n)
  | x0D                  # Carriage Return (CR \r)
  | [x20-x7E]            # Printable ASCII
                         # 16 bit
  | x85                  # Next Line (NEL)
  | [xA0-xD7FF]          # Basic Multilingual Plane (BMP)
  | [xE000-xFFFD]        # Additional Unicode Areas
  | [x010000-x10FFFF]    # 32 bit

为确保JSON 兼容性,YAML 处理器必须允许所有非 C0 字符出现在带引号标量内部。 为确保可读性,不可打印字符在输出时应当被转义, 即使位于此类标量内部也是如此。

注:JSON 带引号标量不能跨多行或 包含制表符,但 YAML 带引号标量可以。

[2] nb-json ::=
    x09              # Tab character
  | [x20-x10FFFF]    # Non-C0-control characters

注:产生式名称 nb-json 在这里表示“non-break JSON compatible”。

5.2. 字符编码

此规范中提到的所有字符都是 Unicode 码位。 每个此类码位会根据所使用的 字符编码写成一个或多个字节。 注意,在 UTF-16 中,xFFFF 以上的字符使用 代理对写成四个字节。

字符编码是呈现细节, 不得用于传达内容信息。

在输入时,YAML 处理器必须支持 UTF-8 和 UTF-16 字符 编码。 为了JSON 兼容性,也必须支持 UTF-32 编码。

如果字符字节顺序标记开头, 则字符编码 会被视为该字节顺序标记所指示的编码。 否则,必须以 ASCII 字符开始。 这允许通过 null(x00) 字符的模式推断编码。

字节顺序标记可以出现在任何文档的开头,然而同一 中的所有文档必须使用相同的 字符编码。

为了允许JSON 兼容性,字节顺序标记也允许出现在 带引号标量内部。 为了可读性,此类内容字节顺序标记在输出时应当被转义

因此,可以通过按顺序将的前几个字节 与下表行匹配来推断编码:

Byte0 Byte1 Byte2 Byte3 编码
显式 BOM x00 x00 xFE xFF UTF-32BE
ASCII 首字符 x00 x00 x00 any UTF-32BE
显式 BOM xFF xFE x00 x00 UTF-32LE
ASCII 首字符 any x00 x00 x00 UTF-32LE
显式 BOM xFE xFF UTF-16BE
ASCII 首字符 x00 any UTF-16BE
显式 BOM xFF xFE UTF-16LE
ASCII 首字符 any x00 UTF-16LE
显式 BOM xEF xBB xBF UTF-8
默认 UTF-8

推荐的输出编码是 UTF-8。 如果使用其他编码,建议使用显式字节顺序标记, 即使第一个字符是 ASCII。

关于字节顺序标记和 Unicode 字符 编码方案的更多信息,请参见 Unicode FAQ17

[3] c-byte-order-mark ::= xFEFF

在示例中,字节顺序标记字符显示为“”。

示例 5.1 字节顺序标记

# Comment only.

# This stream contains no
# documents, only comments.

图例:

示例 5.2 无效的字节顺序标记

- Invalid use of BOM

- Inside a document.
ERROR:
 A BOM must not appear
 inside a document.

5.3. 指示字符

指示符是具有特殊语义的字符。

-”(x2D,连字符)表示块序列条目。

[4] c-sequence-entry ::= '-'

?”(x3F,问号)表示映射键

[5] c-mapping-key ::= '?'

:”(x3A,冒号)表示映射值

[6] c-mapping-value ::= ':'

示例 5.3 块结构指示符

sequence:
- one
- two
mapping:
  ? sky
  : blue
  sea : green
{ "sequence": [
    "one",
    "two" ],
  "mapping": {
    "sky": "blue",
    "sea": "green" } }

,”(x2C,逗号)结束流式集合条目。

[7] c-collect-entry ::= ','

[”(x5B,左方括号)开始一个流式序列

[8] c-sequence-start ::= '['

]”(x5D,右方括号)结束一个流式序列

[9] c-sequence-end ::= ']'

{”(x7B,左花括号)开始一个流式映射

[10] c-mapping-start ::= '{'

}”(x7D,右花括号)结束一个流式映射

[11] c-mapping-end ::= '}'

示例 5.4 流式集合指示符

sequence: [ one, two, ]
mapping: { sky: blue, sea: green }
{ "sequence": [ "one", "two" ],
  "mapping":
    { "sky": "blue", "sea": "green" } }

#”(x23,octothorpe、hash、sharp、pound、 number sign)表示注释

[12] c-comment ::= '#'

示例 5.5 注释指示符

# Comment only.

# This stream contains no
# documents, only comments.

图例:

&”(x26,和号)表示节点的锚点属性

[13] c-anchor ::= '&'

*”(x2A,星号)表示别名节点

[14] c-alias ::= '*'

!”(x21,感叹号)用于指定 节点标签。 它用于表示标签指令标签 属性中使用的标签句柄;用于表示本地标签; 也作为非普通标量非特定标签

[15] c-tag ::= '!'

示例 5.6 节点属性指示符

anchored: !local &anchor value
alias: *anchor
{ "anchored": !local &A1 "value",
  "alias": *A1 }

图例:

|”(7C,竖线)表示字面块标量

[16] c-literal ::= '|'

>”(x3E,大于号)表示折叠块标量

[17] c-folded ::= '>'

示例 5.7 块标量指示符

literal: |
  some
  text
folded: >
  some
  text
{ "literal": "some\ntext\n",
  "folded": "some text\n" }

图例:

'”(x27,撇号,单引号)包围 单引号流式 标量

[18] c-single-quote ::= "'"

"”(x22,双引号)包围双引号流式标量

[19] c-double-quote ::= '"'

示例 5.8 带引号标量指示符

single: 'text'
double: "text"
{ "single": "text",
  "double": "text" }

%”(x25,百分号)表示指令行。

[20] c-directive ::= '%'

示例 5.9 指令指示符

%YAML 1.2
--- text
"text"

图例:

@”(x40,at)和“`” (x60,重音符)被 保留供未来使用。

[21] c-reserved ::=
    '@' | '`'

示例 5.10 保留指示符的无效使用

commercial-at: @text
grave-accent: `text
ERROR:
 Reserved indicators can't
 start a plain scalar.

任意指示字符:

[22] c-indicator ::=
    c-sequence-entry    # '-'
  | c-mapping-key       # '?'
  | c-mapping-value     # ':'
  | c-collect-entry     # ','
  | c-sequence-start    # '['
  | c-sequence-end      # ']'
  | c-mapping-start     # '{'
  | c-mapping-end       # '}'
  | c-comment           # '#'
  | c-anchor            # '&'
  | c-alias             # '*'
  | c-tag               # '!'
  | c-literal           # '|'
  | c-folded            # '>'
  | c-single-quote      # "'"
  | c-double-quote      # '"'
  | c-directive         # '%'
  | c-reserved          # '@' '`'

[”、“]”、“{”、“}” 和 “,” 指示符表示流式 集合中的结构。 因此在某些情况下禁止使用它们,以避免若干 构造中的歧义。 这由相关产生式逐一处理。

[23] c-flow-indicator ::=
    c-collect-entry     # ','
  | c-sequence-start    # '['
  | c-sequence-end      # ']'
  | c-mapping-start     # '{'
  | c-mapping-end       # '}'

5.4. 换行字符

YAML 识别以下 ASCII 换行字符。

[24] b-line-feed ::= x0A
[25] b-carriage-return ::= x0D
[26] b-char ::=
    b-line-feed          # x0A
  | b-carriage-return    # X0D

所有其他字符,包括换页符(x0C),都被视为 非换行字符。 注意,这些字符包括非 ASCII 换行:下一行(x85)、行 分隔符(x2028)和段落 分隔符(x2029)。

YAML 版本 1.1 确实支持上述非 ASCII 换行 字符; 然而,JSON 不支持。 因此,为确保JSON 兼容性,YAML 从版本 1.2 起将它们视为非换行字符。 解析版本 1.1文档的 YAML 1.2 处理器因此应当 将这些换行视为非换行字符,并给出适当警告。

[27] nb-char ::=
  c-printable - b-char - c-byte-order-mark

换行在不同系统中会以不同方式解释,并具有多种 广泛使用的格式。

[28] b-break ::=
    (
      b-carriage-return  # x0A
      b-line-feed
    )                    # x0D
  | b-carriage-return
  | b-line-feed

标量内容内部的换行必须由 YAML 处理器规范化。 每个这样的换行都必须被解析为单个 换行字符。 原始换行格式是一种呈现 细节,不得用于 传达内容信息。

[29] b-as-line-feed ::=
  b-break

标量内容之外,YAML 允许使用任意换行来终止 行。

[30] b-non-content ::=
  b-break

在输出时,YAML 处理器可以自由地使用 最合适的约定发出换行。

在示例中,换行有时会使用“”字形显示,以便 清晰呈现。

示例 5.11 换行字符

|
  Line break (no glyph)
  Line break (glyphed)
"Line break (no glyph)\nLine break (glyphed)\n"

图例:

5.5. 空白字符

YAML 识别两个空白字符:空格制表符

[31] s-space ::= x20
[32] s-tab ::= x09
[33] s-white ::=
  s-space | s-tab

其余(可打印的)非换行字符被视为 非空格字符。

[34] ns-char ::=
  nb-char - s-white

在示例中,制表符显示为字形“”。 空格字符有时显示为字形“·”以便清晰呈现。

示例 5.12 制表符和空格

# Tabs and spaces
quoted:·"Quoted "
block:|
··void main() {
··printf("Hello, world!\n");
··}
{ "quoted": "Quoted \t",
  "block": "void main()
    {\n\tprintf(\"Hello, world!\\n\");\n}\n" }

图例:

5.6. 其他字符

YAML 语法产生式使用以下附加字符 类:

用于数字的十进制数字:

[35] ns-dec-digit ::=
  [x30-x39]             # 0-9

用于转义序列的十六进制数字:

[36] ns-hex-digit ::=
    ns-dec-digit        # 0-9
  | [x41-x46]           # A-F
  | [x61-x66]           # a-f

ASCII 字母(alphabetic)字符:

[37] ns-ascii-letter ::=
    [x41-x5A]           # A-Z
  | [x61-x7A]           # a-z

用于标识符的单词(字母数字)字符:

[38] ns-word-char ::=
    ns-dec-digit        # 0-9
  | ns-ascii-letter     # A-Z a-z
  | '-'                 # '-'

用于标签的 URI 字符,如 URI 规范中所定义18

按约定,除允许的可打印 ASCII 字符之外的任何 URI 字符都会先以 UTF-8 编码,然后每个字节使用 “%”字符转义。 YAML 处理器不得展开此类转义字符。 标签字符必须完全按照其在 YAML 呈现的样子保留和比较,不进行任何处理。

[39] ns-uri-char ::=
    (
      '%'
      ns-hex-digit{2}
    )
  | ns-word-char
  | '#'
  | ';'
  | '/'
  | '?'
  | ':'
  | '@'
  | '&'
  | '='
  | '+'
  | '$'
  | ','
  | '_'
  | '.'
  | '!'
  | '~'
  | '*'
  | "'"
  | '('
  | ')'
  | '['
  | ']'

!”字符用于表示 命名标签句柄的结束;因此 它在标签简写中的使用受到限制。 此外,此类简写不得包含“[”、“]”、“{”、“}” 和“,”字符。 这些字符会与流式 集合结构产生歧义。

[40] ns-tag-char ::=
    ns-uri-char
  - c-tag               # '!'
  - c-flow-indicator

5.7. 转义字符

所有非可打印字符都必须被转义。 YAML 转义序列使用大多数现代计算机 语言中常见的“\” 记法。 每个转义序列都必须被解析为 相应的 Unicode 字符。 原始转义序列是一种呈现 细节,不得用于 传达内容信息。

注意,转义序列只在双引号 标量中解释。 在所有其他标量样式中,“\”字符没有特殊含义,并且 非可打印字符不可用。

[41] c-escape ::= '\'

YAML 转义序列是 C 转义序列的超集:

转义 ASCII null(x00)字符。

[42] ns-esc-null ::= '0'

转义 ASCII bell(x07)字符。

[43] ns-esc-bell ::= 'a'

转义 ASCII 退格(x08) 字符。

[44] ns-esc-backspace ::= 'b'

转义 ASCII 水平制表符(x09) 字符。 这在行首或行尾很有用,可强制前导或尾随 制表符成为内容的一部分。

[45] ns-esc-horizontal-tab ::=
  't' | x09

转义 ASCII 换行(x0A) 字符。

[46] ns-esc-line-feed ::= 'n'

转义 ASCII 垂直制表符(x0B) 字符。

[47] ns-esc-vertical-tab ::= 'v'

转义 ASCII 换页符(x0C) 字符。

[48] ns-esc-form-feed ::= 'f'

转义 ASCII 回车(x0D) 字符。

[49] ns-esc-carriage-return ::= 'r'

转义 ASCII escape(x1B)字符。

[50] ns-esc-escape ::= 'e'

转义 ASCII 空格(x20)字符。 这在行首或行尾很有用,可强制前导或尾随 空格成为内容的一部分。

[51] ns-esc-space ::= x20

转义 ASCII 双引号(x22)。

[52] ns-esc-double-quote ::= '"'

转义 ASCII 斜杠(x2F),用于JSON 兼容性

[53] ns-esc-slash ::= '/'

转义 ASCII 反斜杠(x5C)。

[54] ns-esc-backslash ::= '\'

转义 Unicode 下一行(x85) 字符。

[55] ns-esc-next-line ::= 'N'

转义 Unicode 不间断空格(xA0)字符。

[56] ns-esc-non-breaking-space ::= '_'

转义 Unicode 行分隔符(x2028) 字符。

[57] ns-esc-line-separator ::= 'L'

转义 Unicode 段落分隔符(x2029)字符。

[58] ns-esc-paragraph-separator ::= 'P'

转义 8 位 Unicode 字符。

[59] ns-esc-8-bit ::=
  'x'
  ns-hex-digit{2}

转义 16 位 Unicode 字符。

[60] ns-esc-16-bit ::=
  'u'
  ns-hex-digit{4}

转义 32 位 Unicode 字符。

[61] ns-esc-32-bit ::=
  'U'
  ns-hex-digit{8}

任意转义字符:

[62] c-ns-esc-char ::=
  c-escape         # '\'
  (
      ns-esc-null
    | ns-esc-bell
    | ns-esc-backspace
    | ns-esc-horizontal-tab
    | ns-esc-line-feed
    | ns-esc-vertical-tab
    | ns-esc-form-feed
    | ns-esc-carriage-return
    | ns-esc-escape
    | ns-esc-space
    | ns-esc-double-quote
    | ns-esc-slash
    | ns-esc-backslash
    | ns-esc-next-line
    | ns-esc-non-breaking-space
    | ns-esc-line-separator
    | ns-esc-paragraph-separator
    | ns-esc-8-bit
    | ns-esc-16-bit
    | ns-esc-32-bit
  )

示例 5.13 转义字符

- "Fun with \\"
- "\" \a \b \e \f"
- "\n \r \t \v \0"
- "\  \_ \N \L \P \
  \x41 \u0041 \U00000041"
[ "Fun with \\",
  "\" \u0007 \b \u001b \f",
  "\n \r \t \u000b \u0000",
  "\u0020 \u00a0 \u0085 \u2028 \u2029 A A A" ]

图例:

示例 5.14 无效的转义字符

Bad escapes:
  "\c
  \xq-"
ERROR:
- c is an invalid escaped character.
- q and - are invalid hex digits.

第 6 章 结构产生式

6.1. 缩进空格

在 YAML 块式样式中,结构由 缩进决定。 一般来说,缩进定义为行首的零个或多个空格 字符。

为保持可移植性,制表符不得用于 缩进, 因为不同系统会以不同方式处理制表符。 注意,大多数现代编辑器都可以配置为:按下制表符键时, 插入适当数量的空格

缩进量是一种呈现 细节,不得用于 传达内容信息。

[63]
s-indent(0) ::=
  <empty>

# When n≥0
s-indent(n+1) ::=
  s-space s-indent(n)

块式样式构造在遇到缩进小于 该构造的行时终止。 产生式使用记法“s-indent-less-than(n)”和 “s-indent-less-or-equal(n)”来表达 这一点。

[64]
s-indent-less-than(1) ::=
  <empty>

# When n≥1
s-indent-less-than(n+1) ::=
  s-space s-indent-less-than(n)
  | <empty>
[65]
s-indent-less-or-equal(0) ::=
  <empty>

# When n≥0
s-indent-less-or-equal(n+1) ::=
  s-space s-indent-less-or-equal(n)
  | <empty>

每个节点都必须比其父节点进一步缩进。 所有兄弟节点都必须使用完全相同的缩进级别。 但是每个兄弟节点内容可以 独立地进一步缩进。

示例 6.1 缩进空格

··# Leading comment line spaces are
···# neither content nor indentation.
····
Not indented:
·By one space: |
····By four
······spaces
·Flow style: [    # Leading spaces
···By two,        # in flow style
··Also by two,    # are neither
··→Still by two   # content nor
····]             # indentation.
{ "Not indented": {
    "By one space": "By four\n  spaces\n",
    "Flow style": [
      "By two",
      "Also by two",
      "Still by two" ] } }

图例:

用于表示块集合条目的“-”、“?”和“:”字符 会被人们感知为缩进的一部分。 这由相关产生式逐一处理。

示例 6.2 缩进指示符

?·a
:·-→b
··-··-→c
·····-·d
{ "a":
  [ "b",
    [ "c",
      "d" ] ] }

图例:

6.2. 分隔空格

缩进标量内容之外, YAML 使用空白字符 在一行内的标记之间进行分隔。 注意,此类空白可以安全地包含制表符

分隔空格是一种呈现细节, 不得用于传达 内容信息。

[66] s-separate-in-line ::=
    s-white+
  | <start-of-line>

示例 6.3 分隔空格

-·foo:→·bar
- -·baz
  -baz
[ { "foo": "bar" },
  [ "baz",
    "baz" ] ]

图例:

6.3. 行前缀

标量内容内部,每一行都以非内容行前缀开始。 此前缀始终包括缩进。 对于流式标量样式,它还包括所有前导空白, 其中可以包含制表符

行前缀是一种呈现细节, 不得用于传达 内容信息。

[67]
s-line-prefix(n,BLOCK-OUT) ::= s-block-line-prefix(n)
s-line-prefix(n,BLOCK-IN)  ::= s-block-line-prefix(n)
s-line-prefix(n,FLOW-OUT)  ::= s-flow-line-prefix(n)
s-line-prefix(n,FLOW-IN)   ::= s-flow-line-prefix(n)
[68] s-block-line-prefix(n) ::=
  s-indent(n)
[69] s-flow-line-prefix(n) ::=
  s-indent(n)
  s-separate-in-line?

示例 6.4 行前缀

plain: text
··lines
quoted: "text
··→lines"
block: |
··text
···→lines
{ "plain": "text lines",
  "quoted": "text lines",
  "block": "text\n \tlines\n" }

6.4. 空行

空行由非内容前缀后跟换 行组成。

[70] l-empty(n,c) ::=
  (
      s-line-prefix(n,c)
    | s-indent-less-than(n)
  )
  b-as-line-feed

空行的语义取决于它们所出现的标量样式。 这由相关产生式逐一处理。

示例 6.5 空行

Folding:
  "Empty line
···→
  as a line feed"
Chomping: |
  Clipped empty lines
·
{ "Folding": "Empty line\nas a line feed",
  "Chomping": "Clipped empty lines\n" }

图例:

6.5. 行折叠

行折叠允许为了可读性而断开长行,同时保留 原始长行的语义。 如果一个换行后跟一个空行,它会被修剪;第一个 换行被丢弃,其余的作为内容保留。

[71] b-l-trimmed(n,c) ::=
  b-non-content
  l-empty(n,c)+

否则(下一行不是行),该换行会转换为 单个空格x20)。

[72] b-as-space ::=
  b-break

折叠后的非空行可以以上述任一种换行结束。

[73] b-l-folded(n,c) ::=
  b-l-trimmed(n,c) | b-as-space

示例 6.6 行折叠

>-
  trimmed
··↓
·↓

  as
  space
"trimmed\n\n\nas space"

上述规则共同适用于折叠块式样式标量 流式样式。 折叠会以下列方式区分这些情况:

块折叠

折叠块式样式中,最终换行和尾随空行 会受到截断影响,且从不被折叠。 此外,折叠不适用于包含前导空白的文本行 周围的换行。 注意,这样一个缩进更多的行可能 只由这样的前导空白组成。

块行折叠规则的组合效果是:每个“段落” 被解释为一行,空行被解释为 换行,并且缩进更多行的 格式会被保留。

示例 6.7 块折叠

>
··foo·
·↓
··→·bar

··baz↓
"foo \n\n\t bar\n\nbaz\n"

图例:

流式折叠

流式样式中的折叠提供了更宽松的 语义。 流式样式通常依赖显式指示符而不是 缩进来传达结构。 因此,行中文本之前或之后的空格是一种呈现 细节,不得用于传达内容信息。 一旦所有这样的空格都被丢弃,所有换 行都会无例外地折叠。

流式行折叠规则的组合效果是:每个“段落” 被解释为一行,空行被解释为换 行,并且文本 可以自由地缩进更多,而不会影响 内容信息。

[74] s-flow-folded(n) ::=
  s-separate-in-line?
  b-l-folded(n,FLOW-IN)
  s-flow-line-prefix(n)

示例 6.8 流式折叠

"
··foo·
·
··→·bar

··baz "
" foo\nbar\nbaz "

图例:

6.6. 注释

显式注释由“#”指示符标记。 注释是一种呈现细节,不得用于 传达内容 信息。

注释必须通过空白字符与其他标记分隔

注:为确保JSON 兼容性,YAML 处理器必须允许 省略输入最终注释的换行。 然而,由于这会使许多工具混淆,YAML 处理器 在输出时应当用显式换 行终止

[75] c-nb-comment-text ::=
  c-comment    # '#'
  nb-char*
[76] b-comment ::=
    b-non-content
  | <end-of-input>
[77] s-b-comment ::=
  (
    s-separate-in-line
    c-nb-comment-text?
  )?
  b-comment

示例 6.9 分隔注释

key:····# Comment
  valueeof
{ "key": "value" }

标量内容之外,注释可以单独出现在一行上, 与缩进级别无关。 注意,在标量内容之外,只包含空白 字符的行会被视为注释行。

[78] l-comment ::=
  s-separate-in-line
  c-nb-comment-text?
  b-comment

示例 6.10 注释行

··# Comment↓
···

# This stream contains no
# documents, only comments.

在大多数情况下,当一行可以以注释结束时,YAML 允许其后 跟随额外的注释行。 唯一例外是结束块标量 头的注释。

[79] s-l-comments ::=
  (
      s-b-comment
    | <start-of-line>
  )
  l-comment*

示例 6.11 多行注释

key:····# Comment↓
········# lines↓
  value

{ "key": "value" }

6.7. 分隔行

隐式键被限制在单行内。 在所有其他情况下,YAML 允许标记由多行(可能 为空的)注释分隔。

注意,跟随多行注释分隔的结构必须正确 缩进,即使对分隔 注释行本身没有这样的限制。

[80]
s-separate(n,BLOCK-OUT) ::= s-separate-lines(n)
s-separate(n,BLOCK-IN)  ::= s-separate-lines(n)
s-separate(n,FLOW-OUT)  ::= s-separate-lines(n)
s-separate(n,FLOW-IN)   ::= s-separate-lines(n)
s-separate(n,BLOCK-KEY) ::= s-separate-in-line
s-separate(n,FLOW-KEY)  ::= s-separate-in-line
[81] s-separate-lines(n) ::=
    (
      s-l-comments
      s-flow-line-prefix(n)
    )
  | s-separate-in-line

示例 6.12 分隔空格

{·first:·Sammy,·last:·Sosa·}:
# Statistics:
··hr:··# Home runs
·····65
··avg:·# Average
···0.278
{ { "first": "Sammy",
    "last": "Sosa" }: {
    "hr": 65,
    "avg": 0.278 } }

6.8. 指令

指令是给 YAML 处理器的指示。 此规范定义了两个指令:“YAML”和“TAG”,并保留 所有其他指令供未来使用。 没有定义私有指令的方式。 这是有意为之。

指令是一种呈现细节,不得用于 传达内容 信息。

[82] l-directive ::=
  c-directive            # '%'
  (
      ns-yaml-directive
    | ns-tag-directive
    | ns-reserved-directive
  )
  s-l-comments

每个指令都在单独的非缩进行上指定, 以“%”指示符开头,随后是 指令名称和参数列表。 这些参数的语义取决于具体指令。 YAML 处理器应当忽略未知指令,并给出 适当警告。

[83] ns-reserved-directive ::=
  ns-directive-name
  (
    s-separate-in-line
    ns-directive-parameter
  )*
[84] ns-directive-name ::=
  ns-char+
[85] ns-directive-parameter ::=
  ns-char+

示例 6.13 保留指令

%FOO  bar baz # Should be ignored
               # with a warning.
--- "foo"
"foo"

6.8.1. “YAML” 指令

YAML”指令指定 文档所符合的 YAML 版本。 此规范定义版本“1.2”,包括 YAML 1.1 处理建议。

版本 1.2 的 YAML 处理器必须接受带有显式“%YAML 1.2”指令的文档,也必须接受缺少“YAML”指令的文档。 此类文档被假定符合 1.2 版本规范。 带有指定更高次版本(例如 “%YAML 1.3”)的“YAML”指令的文档 应当在给出适当警告后处理。 带有指定更高主版本(例如 “%YAML 2.0”)的“YAML”指令的文档 应当被拒绝,并给出适当错误消息。

版本 1.2 的 YAML 处理器还必须接受带有显式 “%YAML 1.1”指令的文档。 注意,版本 1.2 大体上是版本 1.1 的超集,其定义目的在于确保 JSON 兼容性。 因此,版本 1.2 的处理器应当将版本 1.1 的文档视为 版本 1.2 来处理,并在不兼容点给出警告(如上文所述的 非 ASCII 换行处理)。

[86] ns-yaml-directive ::=
  "YAML"
  s-separate-in-line
  ns-yaml-version
[87] ns-yaml-version ::=
  ns-dec-digit+
  '.'
  ns-dec-digit+

示例 6.14 “YAML” 指令

%YAML 1.3 # Attempt parsing
           # with a warning
---
"foo"
"foo"

为同一个文档指定多个“YAML”指令是错误的, 即使两次出现给出相同的版本号也是如此。

示例 6.15 无效的重复 YAML 指令

%YAML 1.2
%YAML 1.1
foo
ERROR:
The YAML directive must only be
given at most once per document.

6.8.2. “TAG” 指令

TAG”指令建立一种标签简写表示法,用于指定 节点标签。 每个“TAG”指令都会将一个句柄与一个前缀关联。 这使标签表示法紧凑且可读。

[88] ns-tag-directive ::=
  "TAG"
  s-separate-in-line
  c-tag-handle
  s-separate-in-line
  ns-tag-prefix

示例 6.16 “TAG” 指令

%TAG !yaml! tag:yaml.org,2002:
---
!yaml!str "foo"
"foo"

在同一个文档中,为同一个句柄指定多个“TAG”指令是错误的, 即使两次出现给出相同的前缀也是如此。

示例 6.17 无效的重复 TAG 指令

%TAG ! !foo
%TAG ! !foo
bar
ERROR:
The TAG directive must only
be given at most once per
handle in the same document.

6.8.2.1. 标签句柄

标签句柄与受影响标签 简写的前缀完全匹配。 有三种标签句柄变体:

[89] c-tag-handle ::=
    c-named-tag-handle
  | c-secondary-tag-handle
  | c-primary-tag-handle
主句柄

主标签句柄是单个“!”字符。 这允许为单个“主” 命名空间使用最紧凑的表示法。 默认情况下,与此句柄关联的前缀是“!”。 因此,默认情况下,使用此句柄的简写 会被解释为本地 标签

可以通过提供显式 “TAG”指令,为此句柄关联 不同前缀,从而覆盖默认行为。 这通过简单地添加单个“TAG”指令, 提供了从使用本地标签到使用全局标签的平滑迁移。

[90] c-primary-tag-handle ::= '!'

示例 6.18 主标签句柄

# Private
!foo "bar"
...
# Global
%TAG ! tag:example.com,2000:app/
---
!foo "bar"
!<!foo> "bar"
---
!<tag:example.com,2000:app/foo> "bar"
次句柄

次标签句柄写作“!!”。 这允许为单个“次” 命名空间使用紧凑表示法。 默认情况下,与此句柄关联的前缀是“tag:yaml.org,2002:”。

可以通过提供显式 “TAG”指令并为此句柄关联 不同前缀,来覆盖此默认行为。

[91] c-secondary-tag-handle ::= "!!"

示例 6.19 次标签句柄

%TAG !! tag:example.com,2000:app/
---
!!int 1 - 3 # Interval, not integer
!<tag:example.com,2000:app/int> "1 - 3"
命名句柄

命名标签句柄用“!”字符包围一个非空名称。 句柄名称不得用于标签简写,除非 显式“TAG” 指令已经为它关联了某个前缀。

句柄名称是一种呈现 细节,不得用于 传达内容信息。 特别是,一旦解析完成, YAML 处理器不需要保留 句柄名称。

[92] c-named-tag-handle ::=
  c-tag            # '!'
  ns-word-char+
  c-tag            # '!'

示例 6.20 标签句柄

%TAG !e! tag:example.com,2000:app/
---
!e!foo "bar"
!<tag:example.com,2000:app/foo> "bar"

图例:

6.8.2.2. 标签前缀

有两种标签前缀变体:

[93] ns-tag-prefix ::=
  c-ns-local-tag-prefix | ns-global-tag-prefix
本地标签前缀

如果前缀以“!” 字符开头,则使用该句柄简写 会被展开为本地标签。 注意,此类标签有意不是有效 URI,其语义 特定于应用程序。 特别是,同一中的两个文档可以为相同的 本地标签指定不同的语义。

[94] c-ns-local-tag-prefix ::=
  c-tag           # '!'
  ns-uri-char*

示例 6.21 本地标签前缀

%TAG !m! !my-
--- # Bulb here
!m!light fluorescent
...
%TAG !m! !my-
--- # Color here
!m!light green
!<!my-light> "fluorescent"
---
!<!my-light> "green"
全局标签前缀

如果前缀以非“!”字符开头,则它必须是有效的 URI 前缀,并且应当至少包含方案。 使用关联句柄简写会被展开为全局唯一的 URI 标签,并且其语义在应用程序之间保持一致。 特别是,每个中每个文档都必须为相同的 全局标签指定相同的 语义。

[95] ns-global-tag-prefix ::=
  ns-tag-char
  ns-uri-char*

示例 6.22 全局标签前缀

%TAG !e! tag:example.com,2000:app/
---
- !e!foo "bar"
- !<tag:example.com,2000:app/foo> "bar"

6.9. 节点属性

每个节点除了其内容之外, 还可以有两个可选属性锚点标签。 节点属性可以按任意顺序在节点的内容之前指定。 二者之一或二者都可以省略。

[96] c-ns-properties(n,c) ::=
    (
      c-ns-tag-property
      (
        s-separate(n,c)
        c-ns-anchor-property
      )?
    )
  | (
      c-ns-anchor-property
      (
        s-separate(n,c)
        c-ns-tag-property
      )?
    )

示例 6.23 节点属性

!!str &a1 "foo":
  !!str bar
&a2 baz : *a1
{ &B1 "foo": "bar",
  "baz": *B1 }

6.9.1. 节点标签

标签属性标识由节点呈现原生数据结构的类型。 标签由“!”指示符表示。

[97] c-ns-tag-property ::=
    c-verbatim-tag
  | c-ns-shorthand-tag
  | c-non-specific-tag
逐字标签

标签可以通过用“<”和“>” 字符包围而逐字书写。 在这种情况下,YAML 处理器必须将 逐字标签原样交付给 应用程序。 特别是,逐字标签不受标签 解析影响。 逐字标签必须以“!”(本地 标签)开头,或者是有效 URI (全局标签)。

[98] c-verbatim-tag ::=
  "!<"
  ns-uri-char+
  '>'

示例 6.24 逐字标签

!<tag:yaml.org,2002:str> foo :
  !<!bar> baz
{ "foo": !<!bar> "baz" }

图例:

示例 6.25 无效的逐字标签

- !<!> foo
- !<$:?> bar
ERROR:
- Verbatim tags aren't resolved,
  so ! is invalid.
- The $:? tag is neither a global
  URI tag nor a local tag starting
  with '!'.
标签简写

标签简写由有效标签句柄 后跟非空 后缀组成。 标签句柄必须通过默认方式或通过 使用“TAG”指令 与前缀关联。 生成的已解析标签前缀 与 后缀的连接,并且必须以“!”(本地 标签)开头,或者是有效 URI( 全局标签)。

标签句柄的选择是一种呈现细节,不得用于 传达内容信息。 特别是,一旦解析完成,标签句柄可以被丢弃。

后缀不得包含任何“!”字符。 这会导致标签简写被解释为具有命名标签 句柄。 此外,后缀不得包含“[”、“]”、“{”、“}”和 “,”字符。 这些字符会与流式 集合结构产生歧义。 如果后缀需要指定上述任何受限字符,它们 必须使用“%”字符进行转义。 此行为与 URI 字符转义规则一致 (具体为 URI RFC 第 2.3 节)。

[99] c-ns-shorthand-tag ::=
  c-tag-handle
  ns-tag-char+

示例 6.26 标签简写

%TAG !e! tag:example.com,2000:app/
---
- !local foo
- !!str bar
- !e!tag%21 baz
[ !<!local> "foo",
  !<tag:yaml.org,2002:str> "bar",
  !<tag:example.com,2000:app/tag!> "baz" ]

图例:

示例 6.27 无效的标签简写

%TAG !e! tag:example,2000:app/
---
- !e! foo
- !h!bar baz
ERROR:
- The !e! handle has no suffix.
- The !h! handle wasn't declared.
非特定标签

如果节点没有标签属性,则会被分配一个需要 解析特定标签的 非特定标签。 此非特定标签对于非普通标量是“!”,对于所有 其他节点是“?”。 这是节点样式会对内容 信息产生影响的唯一情况。

标签属性可以被显式设置为“!” 非特定标签。 按约定,这会“禁用”标签解析,强制节点 根据其种类被解释为 “tag:yaml.org,2002:seq”、“tag:yaml.org,2002:map”或 “tag:yaml.org,2002:str”。

没有显式指定“?”非特定标签的方式。 这是有意为之。

[100] c-non-specific-tag ::= '!'

示例 6.28 非特定标签

# Assuming conventional resolution:
- "12"
- 12
- ! 12
[ "12",
  12,
  "12" ]

图例:

6.9.2. 节点锚点

锚点由“&” 指示符表示。 它标记一个节点以供将来引用。 随后可以使用别名节点来表示该 被锚定节点的额外包含。 被锚定的节点不需要被任何别名 节点引用;特别是, 所有节点都被锚定也是有效的。

[101] c-ns-anchor-property ::=
  c-anchor          # '&'
  ns-anchor-name

注意,作为一种序列化细节, 锚点名称会保留在 序列化树中。 然而,它不会反映在表示图中,并且 不得用于传达内容信息。 特别是,一旦表示组成,YAML 处理器不需要保留 锚点名称。

锚点名称不得包含“[”、 “]”、“{”、“}”和“,” 字符。 这些字符会与流式 集合结构产生歧义。

[102] ns-anchor-char ::=
    ns-char - c-flow-indicator
[103] ns-anchor-name ::=
  ns-anchor-char+

示例 6.29 节点锚点

First occurrence: &anchor Value
Second occurrence: *anchor
{ "First occurrence": &A "Value",
  "Second occurrence": *A }

第 7 章 流式样式产生式

YAML 的流式样式可以看作是 JSON 的自然扩展,用于 覆盖为了可读性而折叠长内容行、为节点打标签以控制 构造原生数据结构,以及使用锚点别名来 复用已构造的对象实例。

7.1. 别名节点

先前已序列化节点的后续出现会被呈现别名节点。 该节点的第一次出现必须用锚点标记,以允许 后续出现被呈现为别名 节点。

别名节点由“*” 指示符表示。 别名引用最近的、在其之前出现且具有相同锚点节点。 如果别名节点使用的锚点此前未在 文档中出现,则为错误。 指定一个未被任何别名节点使用的锚点 并不是错误。

注意,别名节点不得指定任何属性内容,因为 它们已经在该节点的第一次出现处指定。

[104] c-ns-alias-node ::=
  c-alias           # '*'
  ns-anchor-name

示例 7.1 别名节点

First occurrence: &anchor Foo
Second occurrence: *anchor
Override anchor: &anchor Bar
Reuse anchor: *anchor
{ "First occurrence": &A "Foo",
  "Override anchor": &B "Bar",
  "Second occurrence": *A,
  "Reuse anchor": *B }

7.2. 空节点

YAML 允许在许多情况下省略节点内容节点具有空内容时,会被解释为 具有空值的普通标量。 此类节点通常会解析为“null”值。

[105] e-scalar ::= ""

在示例中,空标量有时显示为字形“°”以便 清晰呈现。 注意,此字形对应于字符中的一个位置, 而不是一个实际字符。

示例 7.2 空内容

{
  foo : !!str°,
  !!str° : bar,
}
{ "foo": "",
  "": "bar" }

图例:

节点属性节点内容 都是可选的。 这允许出现完全空的节点。 完全空的节点只有在跟随某个明确表示其存在的指示时才有效。

[106] e-node ::=
  e-scalar    # ""

示例 7.3 完全空的流式节点

{
  ? foo :°,
  °: bar,
}
{ "foo": null,
  null : "bar" }

图例:

7.3. 流式标量样式

YAML 提供三种流式标量样式双引号单引号普通(无引号)。 每一种都在可读性和表达能力之间提供不同的权衡。

标量样式是一种呈现细节,不得用于 传达 内容信息;唯一例外是普通 标量会 为标签解析目的而被区分。

7.3.1. 双引号样式

双引号样式由周围的“"”指示符指定。 这是唯一能够通过使用 “\转义序列表达任意字符串的样式。 代价是必须转义“\”和“"”字符。

[107] nb-double-char ::=
    c-ns-esc-char
  | (
        nb-json
      - c-escape          # '\'
      - c-double-quote    # '"'
    )
[108] ns-double-char ::=
  nb-double-char - s-white

当双引号标量包含在隐式键 内部时,它被限制为单行。

[109] c-double-quoted(n,c) ::=
  c-double-quote         # '"'
  nb-double-text(n,c)
  c-double-quote         # '"'
[110]
nb-double-text(n,FLOW-OUT)  ::= nb-double-multi-line(n)
nb-double-text(n,FLOW-IN)   ::= nb-double-multi-line(n)
nb-double-text(n,BLOCK-KEY) ::= nb-double-one-line
nb-double-text(n,FLOW-KEY)  ::= nb-double-one-line
[111] nb-double-one-line ::=
  nb-double-char*

示例 7.4 双引号隐式键

"implicit block key" : [
  "implicit flow key" : value,
 ]
{ "implicit block key":
  [ { "implicit flow key": "value" } ] }

在多行双引号标量中,换行 会受到流式行 折叠影响,这会丢弃任何尾随空白 字符。 也可以转义换行 字符。 在这种情况下,被转义的换行会从 内容中排除,并且位于被转义 换行之前的任何尾随空白字符都会被 保留。 结合转义空白字符的能力,这允许 双引号行在任意位置断开。

[112] s-double-escaped(n) ::=
  s-white*
  c-escape         # '\'
  b-non-content
  l-empty(n,FLOW-IN)*
  s-flow-line-prefix(n)
[113] s-double-break(n) ::=
    s-double-escaped(n)
  | s-flow-folded(n)

示例 7.5 双引号换行

"folded·↓
to a space,→↓
·↓
to a line feed, or·→\↓
·\·→non-content"
"folded to a space,\nto a line feed, or \t \tnon-content"

每一行上的所有前导和尾随空白字符都会从 内容中排除。 因此,每个续行必须至少包含一个非空格 字符。 空行(如有)会作为行折叠的一部分被消费。

[114] nb-ns-double-in-line ::=
  (
    s-white*
    ns-double-char
  )*
[115] s-double-next-line(n) ::=
  s-double-break(n)
  (
    ns-double-char nb-ns-double-in-line
    (
        s-double-next-line(n)
      | s-white*
    )
  )?
[116] nb-double-multi-line(n) ::=
  nb-ns-double-in-line
  (
      s-double-next-line(n)
    | s-white*
  )

示例 7.6 双引号行

"·1st non-empty

·2nd non-empty·
3rd non-empty·"
" 1st non-empty\n2nd non-empty 3rd non-empty "

7.3.2. 单引号样式

单引号样式由周围的“'”指示符指定。 因此,在单引号标量内,此类字符需要重复书写。 这是单引号标量中执行的唯一一种转义形式。 特别是,“\”和“"”字符可以自由使用。 这将单引号标量限制为可打印字符。 此外,只有当一个空格字符被非空格包围时,才能在该处断开长的单引号行。

[117] c-quoted-quote ::= "''"
[118] nb-single-char ::=
    c-quoted-quote
  | (
        nb-json
      - c-single-quote    # "'"
    )
[119] ns-single-char ::=
  nb-single-char - s-white

示例 7.7 单引号字符

'here''s to "quotes"'
"here's to \"quotes\""

图例:

当单引号标量包含在隐式键 内部时,它被限制为单行。

[120] c-single-quoted(n,c) ::=
  c-single-quote    # "'"
  nb-single-text(n,c)
  c-single-quote    # "'"
[121]
nb-single-text(FLOW-OUT)  ::= nb-single-multi-line(n)
nb-single-text(FLOW-IN)   ::= nb-single-multi-line(n)
nb-single-text(BLOCK-KEY) ::= nb-single-one-line
nb-single-text(FLOW-KEY)  ::= nb-single-one-line
[122] nb-single-one-line ::=
  nb-single-char*

示例 7.8 单引号隐式键

'implicit block key' : [
  'implicit flow key' : value,
 ]
{ "implicit block key":
  [ { "implicit flow key": "value" } ] }

所有前导和尾随空白字符都会从 内容中排除。 因此,每个续行必须至少包含一个非空格 字符。 空行(如有)会作为行折叠的一部分被消费。

[123] nb-ns-single-in-line ::=
  (
    s-white*
    ns-single-char
  )*
[124] s-single-next-line(n) ::=
  s-flow-folded(n)
  (
    ns-single-char
    nb-ns-single-in-line
    (
        s-single-next-line(n)
      | s-white*
    )
  )?
[125] nb-single-multi-line(n) ::=
  nb-ns-single-in-line
  (
      s-single-next-line(n)
    | s-white*
  )

示例 7.9 单引号行

'·1st non-empty

·2nd non-empty·
3rd non-empty·'
" 1st non-empty\n2nd non-empty 3rd non-empty "

7.3.3. 普通样式

普通(无引号)样式没有标识性的指示符,也不提供任何 转义形式。 因此,它是最可读、最受限且最上下文 敏感的 样式。 除了受限的字符集之外,普通标量不得为空,也不得 包含前导或尾随空白字符。 只有当一个空格字符被非空格 包围时,才能在该处断开长的普通行。

普通标量不得以大多数指示符开始,因为这 会与其他 YAML 构造产生歧义。 但是,如果后面跟着一个非空格的“安全”字符, 则“:”、“?”和“-指示符可以用作第一个 字符,因为这不会产生歧义。

[126] ns-plain-first(c) ::=
    (
        ns-char
      - c-indicator
    )
  | (
      (
          c-mapping-key       # '?'
        | c-mapping-value     # ':'
        | c-sequence-entry    # '-'
      )
      [ lookahead = ns-plain-safe(c) ]
    )

普通标量绝不能包含“: ”和“ #”字符组合。 此类组合会与映射键/值对注释产生歧义。 此外,在流式集合内部,或用作隐式键时,普通 标量不得包含“[”、 “]”、“{”、“}”和“,”字符。 这些字符会与流式 集合结构产生歧义。

[127]
ns-plain-safe(FLOW-OUT)  ::= ns-plain-safe-out
ns-plain-safe(FLOW-IN)   ::= ns-plain-safe-in
ns-plain-safe(BLOCK-KEY) ::= ns-plain-safe-out
ns-plain-safe(FLOW-KEY)  ::= ns-plain-safe-in
[128] ns-plain-safe-out ::=
  ns-char
[129] ns-plain-safe-in ::=
  ns-char - c-flow-indicator
[130] ns-plain-char(c) ::=
    (
        ns-plain-safe(c)
      - c-mapping-value    # ':'
      - c-comment          # '#'
    )
  | (
      [ lookbehind = ns-char ]
      c-comment          # '#'
    )
  | (
      c-mapping-value    # ':'
      [ lookahead = ns-plain-safe(c) ]
    )

示例 7.10 普通字符

# Outside flow collection:
- ::vector
- ": - ()"
- Up, up, and away!
- -123
- https://example.com/foo#bar
# Inside flow collection:
- [ ::vector,
  ": - ()",
  "Up, up and away!",
  -123,
  https://example.com/foo#bar ]
[ "::vector",
  ": - ()",
  "Up, up, and away!",
  -123,
  "http://example.com/foo#bar",
  [ "::vector",
    ": - ()",
    "Up, up, and away!",
    -123,
    "http://example.com/foo#bar" ] ]

图例:

当普通标量包含在隐式键 内部时,它还被进一步限制为单行。

[131]
ns-plain(n,FLOW-OUT)  ::= ns-plain-multi-line(n,FLOW-OUT)
ns-plain(n,FLOW-IN)   ::= ns-plain-multi-line(n,FLOW-IN)
ns-plain(n,BLOCK-KEY) ::= ns-plain-one-line(BLOCK-KEY)
ns-plain(n,FLOW-KEY)  ::= ns-plain-one-line(FLOW-KEY)
[132] nb-ns-plain-in-line(c) ::=
  (
    s-white*
    ns-plain-char(c)
  )*
[133] ns-plain-one-line(c) ::=
  ns-plain-first(c)
  nb-ns-plain-in-line(c)

示例 7.11 普通隐式键

implicit block key : [
  implicit flow key : value,
 ]
{ "implicit block key":
  [ { "implicit flow key": "value" } ] }

所有前导和尾随空白字符都会从 内容中排除。 因此,每个续行必须至少包含一个非空格 字符。 空行(如有)会作为行折叠的一部分被消费。

[134] s-ns-plain-next-line(n,c) ::=
  s-flow-folded(n)
  ns-plain-char(c)
  nb-ns-plain-in-line(c)
[135] ns-plain-multi-line(n,c) ::=
  ns-plain-one-line(c)
  s-ns-plain-next-line(n,c)*

示例 7.12 普通行

1st non-empty

·2nd non-empty·
3rd non-empty
"1st non-empty\n2nd non-empty 3rd non-empty"

7.4. 流式集合样式

流式集合可以嵌套在块 集合中([FLOW-OUT 上下文])、嵌套在另一个流式集合中([FLOW-IN 上下文]),也可以是 隐式键的一部分([FLOW-KEY 上下文]或[BLOCK-KEY 上下文])。 流式集合条目由“,”指示符终止。 最后的“,”可以省略。 这不会产生歧义,因为流式集合条目永远不能 完全为空

[136]
in-flow(n,FLOW-OUT)  ::= ns-s-flow-seq-entries(n,FLOW-IN)
in-flow(n,FLOW-IN)   ::= ns-s-flow-seq-entries(n,FLOW-IN)
in-flow(n,BLOCK-KEY) ::= ns-s-flow-seq-entries(n,FLOW-KEY)
in-flow(n,FLOW-KEY)  ::= ns-s-flow-seq-entries(n,FLOW-KEY)

7.4.1. 流式序列

流式序列内容由周围的“[”和“]”字符表示。

[137] c-flow-sequence(n,c) ::=
  c-sequence-start    # '['
  s-separate(n,c)?
  in-flow(n,c)?
  c-sequence-end      # ']'

序列条目由“,” 字符分隔。

[138] ns-s-flow-seq-entries(n,c) ::=
  ns-flow-seq-entry(n,c)
  s-separate(n,c)?
  (
    c-collect-entry     # ','
    s-separate(n,c)?
    ns-s-flow-seq-entries(n,c)?
  )?

示例 7.13 流式序列

- [ one, two, ]
- [three ,four]
[ [ "one",
    "two" ],
  [ "three",
    "four" ] ]

任何流式节点都可以用作流式序列条目。 此外,当流式序列条目是带有单个键/值 对映射时,YAML 提供了一种紧凑 表示法

[139] ns-flow-seq-entry(n,c) ::=
  ns-flow-pair(n,c) | ns-flow-node(n,c)

示例 7.14 流式序列条目

[
"double
 quoted", 'single
           quoted',
plain
 text, [ nested ],
single: pair,
]
[ "double quoted",
  "single quoted",
  "plain text",
  [ "nested" ],
  { "single": "pair" } ]

7.4.2. 流式映射

流式映射由周围的“{”和“}”字符表示。

[140] c-flow-mapping(n,c) ::=
  c-mapping-start       # '{'
  s-separate(n,c)?
  ns-s-flow-map-entries(n,in-flow(c))?
  c-mapping-end         # '}'

映射条目由“,” 字符分隔。

[141] ns-s-flow-map-entries(n,c) ::=
  ns-flow-map-entry(n,c)
  s-separate(n,c)?
  (
    c-collect-entry     # ','
    s-separate(n,c)?
    ns-s-flow-map-entries(n,c)?
  )?

示例 7.15 流式映射

- { one : two , three: four , }
- {five: six,seven : eight}
[ { "one": "two",
    "three": "four" },
  { "five": "six",
    "seven": "eight" } ]

如果指定了可选的“?”映射键 指示符,则条目的其余部分 可以完全为空

[142] ns-flow-map-entry(n,c) ::=
    (
      c-mapping-key    # '?' (not followed by non-ws char)
      s-separate(n,c)
      ns-flow-map-explicit-entry(n,c)
    )
  | ns-flow-map-implicit-entry(n,c)
[143] ns-flow-map-explicit-entry(n,c) ::=
    ns-flow-map-implicit-entry(n,c)
  | (
      e-node    # ""
      e-node    # ""
    )

示例 7.16 流式映射条目

{
? explicit: entry,
implicit: entry,
?°°
}
{ "explicit": "entry",
  "implicit": "entry",
  null: null }

通常,YAML 要求“:”映射 值指示符通过分隔之间隔开空白。 此限制的一个好处是,只要“:”字符后面不跟随空白,它就可以用于 普通标量内部。 这允许未加引号的 URL 和时间戳。 它也可能造成混淆,因为“a:1”是一个普通 标量, 而不是键/值对

注意,可以完全 为空,因为其存在 已由“:”指示。

[144] ns-flow-map-implicit-entry(n,c) ::=
    ns-flow-map-yaml-key-entry(n,c)
  | c-ns-flow-map-empty-key-entry(n,c)
  | c-ns-flow-map-json-key-entry(n,c)
[145] ns-flow-map-yaml-key-entry(n,c) ::=
  ns-flow-yaml-node(n,c)
  (
      (
        s-separate(n,c)?
        c-ns-flow-map-separate-value(n,c)
      )
    | e-node    # ""
  )
[146] c-ns-flow-map-empty-key-entry(n,c) ::=
  e-node    # ""
  c-ns-flow-map-separate-value(n,c)
[147] c-ns-flow-map-separate-value(n,c) ::=
  c-mapping-value    # ':'
  [ lookahead ≠ ns-plain-safe(c) ]
  (
      (
        s-separate(n,c)
        ns-flow-node(n,c)
      )
    | e-node    # ""
  )

示例 7.17 流式映射分隔值

{
unquoted·:·"separate",
https://foo.com,
omitted value:°,
°:·omitted key,
}
{ "unquoted": "separate",
  "http://foo.com": null,
  "omitted value": null,
  null: "omitted key" }

为确保JSON 兼容性,如果流式映射内部的 类似 JSON 的,YAML 允许后续 紧邻 “:”指定。 这不会产生歧义,因为所有类似 JSON 的 都由指示符包围。 然而,由于这会大幅降低可读性,YAML 处理器在输出时应当 将与“:分隔开, 即使在这种情况下也应如此。

[148] c-ns-flow-map-json-key-entry(n,c) ::=
  c-flow-json-node(n,c)
  (
      (
        s-separate(n,c)?
        c-ns-flow-map-adjacent-value(n,c)
      )
    | e-node    # ""
  )
[149] c-ns-flow-map-adjacent-value(n,c) ::=
  c-mapping-value          # ':'
  (
      (
        s-separate(n,c)?
        ns-flow-node(n,c)
      )
    | e-node    # ""
  )

示例 7.18 流式映射相邻值

{
"adjacent":value,
"readable"value,
"empty":°
}
{ "adjacent": "value",
  "readable": "value",
  "empty": null }

如果映射包含单个键/值对,则可以在流式序列内部 使用一种更紧凑的表示法。 这种表示法不需要周围的“{”和“}”字符。 注意,在这种情况下无法为该映射 指定任何节点属性

示例 7.19 单对流式映射

[
foo: bar
]
[ { "foo": "bar" } ]

图例:

如果显式指定“?”指示符, 解析就是明确的,并且 语法与一般情况相同。

[150] ns-flow-pair(n,c) ::=
    (
      c-mapping-key     # '?' (not followed by non-ws char)
      s-separate(n,c)
      ns-flow-map-explicit-entry(n,c)
    )
  | ns-flow-pair-entry(n,c)

示例 7.20 单对显式条目

[
? foo
 bar : baz
]
[ { "foo bar": "baz" } ]

如果省略“?”指示符,解析需要越过隐式 键才能将其识别为键。 为限制所需的前瞻量,“:”指示符必须出现在 距开头最多 1024 个 Unicode 字符之内。 此外,被限制为单行。

注意,YAML 允许任意节点用作。 特别是,可以是序列映射。 因此,如果没有上述限制,实用的一遍式解析将无法实现。

[151] ns-flow-pair-entry(n,c) ::=
    ns-flow-pair-yaml-key-entry(n,c)
  | c-ns-flow-map-empty-key-entry(n,c)
  | c-ns-flow-pair-json-key-entry(n,c)
[152] ns-flow-pair-yaml-key-entry(n,c) ::=
  ns-s-implicit-yaml-key(FLOW-KEY)
  c-ns-flow-map-separate-value(n,c)
[153] c-ns-flow-pair-json-key-entry(n,c) ::=
  c-s-implicit-json-key(FLOW-KEY)
  c-ns-flow-map-adjacent-value(n,c)
[154] ns-s-implicit-yaml-key(c) ::=
  ns-flow-yaml-node(0,c)
  s-separate-in-line?
  /* At most 1024 characters altogether */
[155] c-s-implicit-json-key(c) ::=
  c-flow-json-node(0,c)
  s-separate-in-line?
  /* At most 1024 characters altogether */

示例 7.21 单对隐式条目

- [ YAML·: separate ]
- [ °: empty key entry ]
- [ {JSON: like}:adjacent ]
[ [ { "YAML": "separate" } ],
  [ { null: "empty key entry" } ],
  [ { { "JSON": "like" }: "adjacent" } ] ]

示例 7.22 无效的隐式键

[ foo
 bar: invalid,
 "foo_...>1K characters..._bar": invalid ]
ERROR:
- The foo bar key spans multiple lines
- The foo...bar key is too long

7.5. 流式节点

类似 JSON 的流式样式都有显式的起始和 结束指示符。 唯一不具备此属性的流式样式普通标量。 注意,没有任何“类似 JSON”的样式实际上可被 JSON 接受。 即使双引号样式也是 JSON 字符串 格式的超集。

[156] ns-flow-yaml-content(n,c) ::=
  ns-plain(n,c)
[157] c-flow-json-content(n,c) ::=
    c-flow-sequence(n,c)
  | c-flow-mapping(n,c)
  | c-single-quoted(n,c)
  | c-double-quoted(n,c)
[158] ns-flow-content(n,c) ::=
    ns-flow-yaml-content(n,c)
  | c-flow-json-content(n,c)

示例 7.23 流式内容

- [ a, b ]
- { a: b }
- "a"
- 'b'
- c
[ [ "a", "b" ],
  { "a": "b" },
  "a",
  "b",
  "c" ]

完整的流式节点也具有可选的 节点属性,但别名 节点除外,别名节点引用被锚定节点属性

[159] ns-flow-yaml-node(n,c) ::=
    c-ns-alias-node
  | ns-flow-yaml-content(n,c)
  | (
      c-ns-properties(n,c)
      (
          (
            s-separate(n,c)
            ns-flow-yaml-content(n,c)
          )
        | e-scalar
      )
    )
[160] c-flow-json-node(n,c) ::=
  (
    c-ns-properties(n,c)
    s-separate(n,c)
  )?
  c-flow-json-content(n,c)
[161] ns-flow-node(n,c) ::=
    c-ns-alias-node
  | ns-flow-content(n,c)
  | (
      c-ns-properties(n,c)
      (
        (
          s-separate(n,c)
          ns-flow-content(n,c)
        )
        | e-scalar
      )
    )

示例 7.24 流式节点

- !!str "a"
- 'b'
- &anchor "c"
- *anchor
- !!str°
[ "a",
  "b",
  "c",
  "c",
  "" ]

第 8 章 块式样式产生式

YAML 的块式样式使用缩进而不是指示符来表示 结构。 这会得到一种更适合人类阅读(尽管不那么紧凑)的表示法。

8.1. 块标量样式

YAML 提供两种块标量样式字面折叠。 每一种都在可读性和表达能力之间提供不同的权衡。

8.1.1. 块标量头

块标量由位于内容本身之前的中给出的若干指示符控制。 此头后跟一个非内容换行,可选地带有 注释。 这是唯一一种注释后不得跟随额外 注释行的情况。

注:有关 t 变量的定义,请参见 产生式参数

[162] c-b-block-header(t) ::=
  (
      (
        c-indentation-indicator
        c-chomping-indicator(t)
      )
    | (
        c-chomping-indicator(t)
        c-indentation-indicator
      )
  )
  s-b-comment

示例 8.1 块标量头

- | # Empty header↓
 literal
- >1 # Indentation indicator↓
 ·folded
- |+ # Chomping indicator↓
 keep

- >1- # Both indicators↓
 ·strip
[ "literal\n",
  " folded\n",
  "keep\n\n",
  " strip" ]

8.1.1.1. 块缩进指示符

每个块标量都有一个内容缩进级别。 块标量的内容会排除每行开头的一定数量空格, 直到内容缩进级别为止。

如果块标量有缩进指示符,则该块标量的内容缩进 级别等于块标量自身的缩进级别加上缩进指示符字符的整数值。

如果没有给出缩进指示符,则内容缩进级别 等于内容中第一个非空行的前导空格数量。 如果没有非空行,则内容缩进级别等于 最长行上的空格数量。

如果任何非空行不是以数量 大于或等于内容缩进级别的空格开始,则为错误。

如果任何前导空行包含的空格 多于第一个非空行,则为错误。

YAML 处理器应当只在检测会失败的情况下 发出显式缩进指示符。

[163] c-indentation-indicator ::=
  [x31-x39]    # 1-9

示例 8.2 块缩进指示符

- |°
·detected
- >°
·
··
··# detected
- |1
··explicit
- >°
··detected
[ "detected\n",
  "\n\n# detected\n",
  " explicit\n",
  "\t\ndetected\n" ]

示例 8.3 无效的块标量缩进指示符

- |
··
·text
- >
··text
·text
- |2
·text
ERROR:
- A leading all-space line must
  not have too many spaces.
- A following text line must
  not be less indented.
- The text is less indented
  than the indicated level.

8.1.1.2. 块截断指示符

截断控制如何解释最终换行和 尾随空行。 YAML 提供三种截断方法:

剥离

剥离由“-”截断指示符指定。 在这种情况下,最终换行以及任何尾随 空行都会从标量内容中 排除。

裁剪

裁剪是在没有指定显式截断指示符时使用的默认行为。 在这种情况下,最终换行字符会保留在标量 内容中。 但是,任何尾随空行都会从标量内容中排除。

保留

保留由“+”截断指示符指定。 在这种情况下,最终换行以及任何尾随 空行都被视为标量内容的一部分。 这些额外行不受折叠影响。

所使用的截断方法是一种呈现 细节,不得用于 传达内容信息。

[164]
c-chomping-indicator(STRIP) ::= '-'
c-chomping-indicator(KEEP)  ::= '+'
c-chomping-indicator(CLIP)  ::= ""

块标量的最终换行的解释,由块标量 头中指定的截断指示符控制。

[165]
b-chomped-last(STRIP) ::= b-non-content  | <end-of-input>
b-chomped-last(CLIP)  ::= b-as-line-feed | <end-of-input>
b-chomped-last(KEEP)  ::= b-as-line-feed | <end-of-input>

示例 8.4 截断最终换行

strip: |-
  text
clip: |
  text
keep: |+
  text
{ "strip": "text",
  "clip": "text\n",
  "keep": "text\n" }

跟随块标量的尾随空行的解释,也由块 标量 头中指定的截断指示符控制。

[166]
l-chomped-empty(n,STRIP) ::= l-strip-empty(n)
l-chomped-empty(n,CLIP)  ::= l-strip-empty(n)
l-chomped-empty(n,KEEP)  ::= l-keep-empty(n)
[167] l-strip-empty(n) ::=
  (
    s-indent-less-or-equal(n)
    b-non-content
  )*
  l-trail-comments(n)?
[168] l-keep-empty(n) ::=
  l-empty(n,BLOCK-IN)*
  l-trail-comments(n)?

显式注释行可以跟随尾随空行。 为避免歧义,第一条此类注释行的缩进必须小于块标量内容。 额外的注释行(如有)不受此限制。 这是唯一一种注释行的缩进受到约束的情况。

[169] l-trail-comments(n) ::=
  s-indent-less-than(n)
  c-nb-comment-text
  b-comment
  l-comment*

示例 8.5 截断尾随行

# Strip
  # Comments:
strip: |-
  # text↓
··⇓
·# Clip
··# comments:

clip: |
  # text↓
·↓
·# Keep
··# comments:

keep: |+
  # text↓

·# Trail
··# comments.
{ "strip": "# text",
  "clip": "# text\n",
  "keep": "# text\n\n" }

如果块标量只由空行组成,则这些行 被视为尾随行,因此会受到截断影响。

示例 8.6 空标量截断

strip: >-

clip: >

keep: |+

{ "strip": "",
  "clip": "",
  "keep": "\n" }

8.1.2. 字面样式

字面样式由“|”指示符表示。 它是最简单、最受限且最可读的标量样式

[170] c-l+literal(n) ::=
  c-literal                # '|'
  c-b-block-header(t)
  l-literal-content(n+m,t)

示例 8.7 字面标量

|↓
·literal↓
·→text↓

"literal\n\ttext\n"

图例:

在字面标量内部,所有(已缩进的)字符都被视为 内容,包括空白 字符。 注意,所有换行字符都会被规范化。 此外,空行不会被折叠, 但最终换行和 尾随空行会被截断

字面标量内部没有转义字符的方式。 这将其限制为可打印字符。 此外,也没有办法断开一条长的字面行。

[171] l-nb-literal-text(n) ::=
  l-empty(n,BLOCK-IN)*
  s-indent(n) nb-char+
[172] b-nb-literal-next(n) ::=
  b-as-line-feed
  l-nb-literal-text(n)
[173] l-literal-content(n,t) ::=
  (
    l-nb-literal-text(n)
    b-nb-literal-next(n)*
    b-chomped-last(t)
  )?
  l-chomped-empty(n,t)

示例 8.8 字面内容

|
·
··
··literal
···
··
··text·# Comment
"\n\nliteral\n·\n\ntext\n"

8.1.3. 折叠样式

折叠样式由“>”指示符表示。 它类似于字面样式;但是,折叠标量会受到 行折叠影响。

[174] c-l+folded(n) ::=
  c-folded                 # '>'
  c-b-block-header(t)
  l-folded-content(n+m,t)

示例 8.9 折叠标量

>↓
·folded↓
·text↓

"folded text\n"

图例:

折叠允许在单个空格字符 分隔两个非空格字符的任意位置断开长行。

[175] s-nb-folded-text(n) ::=
  s-indent(n)
  ns-char
  nb-char*
[176] l-nb-folded-lines(n) ::=
  s-nb-folded-text(n)
  (
    b-l-folded(n,BLOCK-IN)
    s-nb-folded-text(n)
  )*

示例 8.10 折叠行

>

·folded
·line·next
·line↓
   * bullet

   * list
   * lines

·last
·line↓

# Comment
"\nfolded line\nnext line\n  \
* bullet\n \n  * list\n  \
* lines\n\nlast line\n"

(以下三个示例重复此示例,每个示例突出显示 不同的产生式。)

空白字符开始的行 (缩进更多的行)不会被 折叠

[177] s-nb-spaced-text(n) ::=
  s-indent(n)
  s-white
  nb-char*
[178] b-l-spaced(n) ::=
  b-as-line-feed
  l-empty(n,BLOCK-IN)*
[179] l-nb-spaced-lines(n) ::=
  s-nb-spaced-text(n)
  (
    b-l-spaced(n)
    s-nb-spaced-text(n)
  )*

示例 8.11 缩进更多的行

>

 folded
 line

 next
 line
···* bullet

···* list
···* lines↓

 last
 line

# Comment
"\nfolded line\nnext line\n  \
* bullet\n \n  * list\n  \
* lines\n\nlast line\n"

分隔折叠行和缩进更多行的换行空行 也不会被折叠

[180] l-nb-same-lines(n) ::=
  l-empty(n,BLOCK-IN)*
  (
      l-nb-folded-lines(n)
    | l-nb-spaced-lines(n)
  )
[181] l-nb-diff-lines(n) ::=
  l-nb-same-lines(n)
  (
    b-as-line-feed
    l-nb-same-lines(n)
  )*

示例 8.12 空分隔行

>

 folded
 line

 next
 line
   * bullet

   * list
   * lines

 last
 line

# Comment
"\nfolded line\nnext line\n  \
* bullet\n \n  * list\n  \
* lines\n\nlast line\n"

图例:

最终换行和尾随空行(如有)会受到 截断影响,并且从不被折叠

[182] l-folded-content(n,t) ::=
  (
    l-nb-diff-lines(n)
    b-chomped-last(t)
  )?
  l-chomped-empty(n,t)

示例 8.13 最终空行

>

 folded
 line

 next
 line
   * bullet

   * list
   * lines

 last
 line

# Comment
"\nfolded line\nnext line\n  \
* bullet\n \n  * list\n  \
* lines\n\nlast line\n"

8.2. 块集合样式

为了可读性,块集合样式不由任何指示符表示。 相反,YAML 使用前瞻方法:只有当看到键/值对序列 条目时,才将块集合与普通标量区分开。

8.2.1. 块序列

块序列只是由一系列节点组成,每个节点都由前导 “-”指示符表示。 “-”指示符必须通过分隔节点之间隔开空白。 这允许“-”在后面跟随非空格字符时 用作普通标量的第一个字符 (例如“-42”)。

[183] l+block-sequence(n) ::=
  (
    s-indent(n+1+m)
    c-l-block-seq-entry(n+1+m)
  )+
[184] c-l-block-seq-entry(n) ::=
  c-sequence-entry    # '-'
  [ lookahead ≠ ns-char ]
  s-l+block-indented(n,BLOCK-IN)

示例 8.14 块序列

block sequence:
··- one↓
  - two : three↓
{ "block sequence": [
    "one",
    { "two": "three" } ] }

图例:

条目节点可以是完全 空的,也可以是嵌套块节点,或者 使用紧凑行内表示法。 当条目本身是嵌套块 集合时,可以使用紧凑表示法。 在这种情况下,“-”指示符 和随后的空格都被视为嵌套集合缩进的一部分。 注意,不可能为这样的 集合指定节点属性

[185] s-l+block-indented(n,c) ::=
    (
      s-indent(m)
      (
          ns-l-compact-sequence(n+1+m)
        | ns-l-compact-mapping(n+1+m)
      )
    )
  | s-l+block-node(n,c)
  | (
      e-node    # ""
      s-l-comments
    )
[186] ns-l-compact-sequence(n) ::=
  c-l-block-seq-entry(n)
  (
    s-indent(n)
    c-l-block-seq-entry(n)
  )*

示例 8.15 块序列条目类型

-° # Empty
- |
 block node
-·- one # Compact
··- two # sequence
- one: two # Compact mapping
[ null,
  "block node\n",
  [ "one", "two" ],
  { "one": "two" } ]

8.2.2. 块映射

块映射是一系列条目,每个条目呈现一个键/值 对

[187] l+block-mapping(n) ::=
  (
    s-indent(n+1+m)
    ns-l-block-map-entry(n+1+m)
  )+

示例 8.16 块映射

block mapping:
·key: value↓
{ "block mapping": {
    "key": "value" } }

图例:

如果指定了“?”指示符,则 可选值节点必须在单独一行上指定,并由“:” 指示符表示。 注意,YAML 在这里允许使用与上文为块序列条目 描述的相同紧凑行内 表示法

[188] ns-l-block-map-entry(n) ::=
    c-l-block-map-explicit-entry(n)
  | ns-l-block-map-implicit-entry(n)
[189] c-l-block-map-explicit-entry(n) ::=
  c-l-block-map-explicit-key(n)
  (
      l-block-map-explicit-value(n)
    | e-node                        # ""
  )
[190] c-l-block-map-explicit-key(n) ::=
  c-mapping-key                     # '?' (not followed by non-ws char)
  s-l+block-indented(n,BLOCK-OUT)
[191] l-block-map-explicit-value(n) ::=
  s-indent(n)
  c-mapping-value                   # ':' (not followed by non-ws char)
  s-l+block-indented(n,BLOCK-OUT)

示例 8.17 显式块映射条目

? explicit key # Empty value↓°
? |
  block key↓
:·- one # Explicit compact
··- two # block value↓
{ "explicit key": null,
  "block key\n": [
    "one",
    "two" ] }

如果省略“?”指示符,解析需要像在流式 映射单个键/值对中一样,越过 隐式键。 因此,此类受到相同限制;它们被限制为 单行,并且不得跨越超过 1024 个 Unicode 字符。

[192] ns-l-block-map-implicit-entry(n) ::=
  (
      ns-s-block-map-implicit-key
    | e-node    # ""
  )
  c-l-block-map-implicit-value(n)
[193] ns-s-block-map-implicit-key ::=
    c-s-implicit-json-key(BLOCK-KEY)
  | ns-s-implicit-yaml-key(BLOCK-KEY)

在这种情况下,可以指定在与隐式 键相同的行上。 但请注意,在块映射中,绝不能紧邻 “:”,因为这会大幅降低 可读性,并且并非JSON 兼容性所需(不同于流式映射中的情况)。

行内没有紧凑表示法。 此外,虽然隐式键及其后的都可以为空, “:”指示符仍然是必需的。 这避免了与多行普通标量的潜在歧义。

[194] c-l-block-map-implicit-value(n) ::=
  c-mapping-value           # ':' (not followed by non-ws char)
  (
      s-l+block-node(n,BLOCK-OUT)
    | (
        e-node    # ""
        s-l-comments
      )
  )

示例 8.18 隐式块映射条目

plain key: in-line value
°:° # Both empty
"quoted key":
- entry
{ "plain key": "in-line value",
  null: null,
  "quoted key": [ "entry" ] }

也可以使用紧凑行内表示法。 此紧凑表示法可以嵌套在块序列和显式块 映射条目内部。 注意,不可能为这样的嵌套 映射指定节点属性

[195] ns-l-compact-mapping(n) ::=
  ns-l-block-map-entry(n)
  (
    s-indent(n)
    ns-l-block-map-entry(n)
  )*

示例 8.19 紧凑块映射

- sun: yellow↓
- ? earth: blue↓
  : moon: white↓
[ { "sun": "yellow" },
  { { "earth": "blue" }:
      { "moon": "white" } } ]

8.2.3. 块节点

YAML 允许将流式节点嵌入块集合内部(但不允许 反过来)。 流式节点缩进必须 比父 块集合至少多一个空格。 注意,流式节点可以从下一行开始。

正是在这一点上,解析需要 区分普通 标量和开始嵌套块映射隐式键

[196] s-l+block-node(n,c) ::=
    s-l+block-in-block(n,c)
  | s-l+flow-in-block(n)
[197] s-l+flow-in-block(n) ::=
  s-separate(n+1,FLOW-OUT)
  ns-flow-node(n+1,FLOW-OUT)
  s-l-comments

示例 8.20 块节点类型

-
··"flow in block"↓>
 Block scalar↓!!map # Block collection
  foo : bar↓
[ "flow in block",
  "Block scalar\n",
  { "foo": "bar" } ]

节点属性可以跨越多行。 在这种情况下,无论块集合 条目的缩进如何,它们都必须比 块集合至少多缩进一个空格

[198] s-l+block-in-block(n,c) ::=
    s-l+block-scalar(n,c)
  | s-l+block-collection(n,c)
[199] s-l+block-scalar(n,c) ::=
  s-separate(n+1,c)
  (
    c-ns-properties(n+1,c)
    s-separate(n+1,c)
  )?
  (
      c-l+literal(n)
    | c-l+folded(n)
  )

示例 8.21 块标量节点

literal: |2
··value
folded:
···!foo
··>1
·value
{ "literal": "value",
  "folded": !<!foo> "value" }

由于人们会将“-”指示符 感知为缩进,嵌套块 序列可以少缩进一个空格以补偿;当然,如果嵌套在另一个块序列内部则例外([BLOCK-OUT 上下文]相对于 [BLOCK-IN 上下文])。

[200] s-l+block-collection(n,c) ::=
  (
    s-separate(n+1,c)
    c-ns-properties(n+1,c)
  )?
  s-l-comments
  (
      seq-space(n,c)
    | l+block-mapping(n)
  )
[201] seq-space(n,BLOCK-OUT) ::= l+block-sequence(n-1)
    seq-space(n,BLOCK-IN)  ::= l+block-sequence(n)

示例 8.22 块集合节点

sequence: !!seq
- entry
- !!seq
 - nested
mapping: !!map
 foo: bar
{ "sequence": [
    "entry",
    [ "nested" ] ],
  "mapping": { "foo": "bar" } }

第 9 章 文档流产生式

9.1. 文档

YAML 字符可以包含多个文档。 每个文档都完全独立于其余文档。

9.1.1. 文档前缀

文档前面可以有一个前缀,用于指定字符编码 和可选的注释行。 注意,流中的所有文档都必须使用相同的字符编码。 但是,对流中的每个文档使用字节顺序标记重新指定编码是有效的。

可选前缀的存在并不一定表示实际文档的存在。

[202] l-document-prefix ::=
  c-byte-order-mark?
  l-comment*

示例 9.1 文档前缀

⇔# Comment
# lines
Document
"Document"

图例:

9.1.2. 文档标记

使用指令会产生潜在歧义。 在一行开头出现“%”字符是有效的 (例如,作为普通标量第二行的第一个 字符)。 那么,如何区分实际指令和恰好以“%”字符开头的内容行呢?

解决方案是使用两种特殊的标记行来控制 指令的处理,一个位于文档开头, 一个位于末尾。

文档开头,以“%”字符开头的行会被 假定为指令。 (可能为空的)指令列表由一个指令结束 标记行终止。 跟随此标记的行可以安全地使用“%”作为第一个字符。

文档末尾,使用文档结束标记行来 通知解析器重新开始扫描指令

此可选文档后缀的存在并不一定表示实际的后续 文档存在。

因此很显然,实际内容行被禁止以 这两种标记中的任何一种开头。

[203] c-directives-end ::= "---"
[204] c-document-end ::=
  "..."    # (not followed by non-ws char)
[205] l-document-suffix ::=
  c-document-end
  s-l-comments
[206] c-forbidden ::=
  <start-of-line>
  (
      c-directives-end
    | c-document-end
  )
  (
      b-char
    | s-white
    | <end-of-input>
  )

示例 9.2 文档标记

%YAML 1.2
---
Document
... # Suffix
"Document"

9.1.3. 裸文档

裸文档不以任何指令标记行开始。 这样的文档非常“干净”,因为它们除了内容之外不包含任何东西。 在这种情况下,第一条非注释行不得以“%”作为第一个 字符开头。

文档节点会像拥有一个在 -1 个 空格缩进的父节点那样 进行缩进。 由于节点必须比其父节点缩进,这允许 文档的节点在零个或更多空格缩进

[207] l-bare-document ::=
  s-l+block-node(-1,BLOCK-IN)
  /* Excluding c-forbidden content */

示例 9.3 裸文档

Bare
document
...
# No document
...
|
%!PS-Adobe-2.0 # Not the first line
"Bare document"
---
"%!PS-Adobe-2.0\n"

图例:

9.1.4. 显式文档

显式文档以一个显式指令结束 标记行开始,但 没有指令。 由于文档的存在由此标记表示, 文档本身可以完全 为空

[208] l-explicit-document ::=
  c-directives-end
  (
      l-bare-document
    | (
        e-node    # ""
        s-l-comments
      )
  )

示例 9.4 显式文档

---
{ matches
% : 20 }
...
---
# Empty
...
{ "matches %": 20 }
---
null

9.1.5. 指令文档

指令文档以一些指令开始,随后是一个显式 指令结束标记行。

[209] l-directive-document ::=
  l-directive+
  l-explicit-document

示例 9.5 指令文档

%YAML 1.2
--- |
%!PS-Adobe-2.0
...
%YAML 1.2
---
# Empty
...
"%!PS-Adobe-2.0\n"
---
null

9.2. 流

YAML 由零个或多个文档组成。 后续文档需要某种分隔标记行。 如果文档未由文档结束标记行终止,则 后续文档必须以指令结束标记行开始。

[210] l-any-document ::=
    l-directive-document
  | l-explicit-document
  | l-bare-document
[211] l-yaml-stream ::=
  l-document-prefix*
  l-any-document?
  (
      (
        l-document-suffix+
        l-document-prefix*
        l-any-document?
      )
    | c-byte-order-mark
    | l-comment
    | l-explicit-document
  )*

示例 9.6 流

Document
---
# Empty
...
%YAML 1.2
---
matches %: 20
"Document"
---
null
---
{ "matches %": 20 }

如果一个字节序列作为整体符合上述 l-yaml-stream 产生式,则它是一个格式良好的流

第 10 章 推荐模式

YAML 模式是一组标签与一种用于解析非特定标签的机制的组合。

10.1. 故障安全模式

故障安全模式保证可与任何 YAML 文档配合使用。 因此,它是通用 YAML 工具推荐使用的模式。 因此,YAML 处理器应当支持此模式,至少作为一个 选项。

10.1.1. 标签

10.1.1.1. 通用映射

URI

tag:yaml.org,2002:map

种类

映射

定义

表示一个关联容器,其中每个在关联中是唯一的,并且准确映射到一个。 YAML 不限制的类型;特别是, 它们不限于标量。 到原生类型的示例绑定包括 Perl 的 hash、 Python 的 dictionary 和 Java 的 Hashtable。

示例 10.1 !!map 示例

Block style: !!map
  Clark : Evans
  Ingy  : döt Net
  Oren  : Ben-Kiki

Flow style: !!map { Clark: Evans, Ingy: döt Net, Oren: Ben-Kiki }

10.1.1.2. 通用序列

URI

tag:yaml.org,2002:seq

种类

序列

定义

表示一个以从零开始的连续整数索引的集合。 到原生类型的示例绑定包括 Perl 的 array、 Python 的 list 或 tuple,以及 Java 的 array 或 Vector。

示例 10.2 !!seq 示例

Block style: !!seq
- Clark Evans
- Ingy döt Net
- Oren Ben-Kiki

Flow style: !!seq [ Clark Evans, Ingy döt Net, Oren Ben-Kiki ]

10.1.1.3. 通用字符串

URI

tag:yaml.org,2002:str

种类

标量

定义

表示一个 Unicode 字符串,即零个或更多 Unicode 字符的序列。 该类型通常会绑定原生语言的字符串类型,或者 对于缺少此类类型的语言(例如 C),绑定到字符数组。

规范形式:

显而易见。

示例 10.3 !!str 示例

Block style: !!str |-
  String: just a theory.

Flow style: !!str "String: just a theory."

10.1.2. 标签解析

所有具有“!”非特定标签的节点,按标准约定解析为“tag:yaml.org,2002:seq”、“tag:yaml.org,2002:map”或 “tag:yaml.org,2002:str”,具体取决于 其种类

所有具有“?”非特定标签的节点会保持未解析。 这会约束应用程序处理部分表示

10.2. JSON 模式

JSON 模式是大多数现代计算机语言的最低公分母,并允许 解析 JSON 文件。 因此,YAML 处理器应当支持此模式,至少作为一个 选项。 也强烈建议其他模式基于它。

10.2.1. 标签

故障安全模式定义的标签之外,JSON 模式还使用以下标签

10.2.1.1. 空

URI

tag:yaml.org,2002:null

种类

标量

定义

表示缺少值。 这通常会绑定原生的类 null 值(例如 Perl 中的 undef, Python 中的 None)。 注意,null 与空字符串不同。 此外,具有某个和 null 映射条目是有效的,并且 与映射中没有该不同。

规范形式

null

示例 10.4 !!null 示例

!!null null: value for null key
key with null value: !!null null

10.2.1.2. 布尔值

URI

tag:yaml.org,2002:bool

种类

标量

定义

表示一个真/假值。 在没有原生布尔类型的语言 (例如 C)中,它们通常会 绑定到原生整数类型,使用 1 表示 true,0 表示 false。

规范形式

truefalse

示例 10.5 !!bool 示例

YAML is a superset of JSON: !!bool true
Pluto is a planet: !!bool false

10.2.1.3. 整数

URI

tag:yaml.org,2002:int

种类

标量

定义

表示任意大小的有限数学整数。 如果可能,此类型的标量应绑定原生整数数据类型。

某些语言(例如 Perl)只提供一种“number”类型,同时允许 整数和浮点值。 只要整数能够正确往返,YAML 处理器就可以 对整数使用此类类型。

在某些语言(例如 C)中,整数可能溢出原生类型的 存储能力。 YAML 处理器可以将此类值作为错误拒绝、带警告截断, 或找到其他方式使其能够往返。 一般来说,可用 32 个二进制位表示的整数应能安全地 通过大多数系统往返。

规范形式

十进制整数表示法,负值带有前导“-”字符, 匹配正则表达式 0 | -? [1-9] [0-9]*

示例 10.6 !!int 示例

negative: !!int -12
zero: !!int 0
positive: !!int 34

10.2.1.4. 浮点数

URI

tag:yaml.org,2002:float

种类

标量

定义

表示实数的近似值, 包括三个特殊值 (正无穷、负无穷和“非数”)。

某些语言(例如 Perl)只提供一种“number”类型,同时允许 整数和浮点值。 只要浮点数能够正确往返,YAML 处理器就可以 对浮点数使用此类类型。

并非所有浮点值都能精确存储在给定的原生类型中。 因此,float 值在往返时可能会改变“一小点”。 支持的范围和精度取决于实现,不过 32 位 IEEE 浮点数应当是安全的。 由于 YAML 没有指定特定精度,使用浮点数作为 映射键需要非常谨慎,并且不推荐。

规范形式

0.inf-.inf.nan 或匹配以下正则 表达式的科学记数法
-? [1-9] ( \. [0-9]* [1-9] )? ( e [-+] [1-9] [0-9]* )?

示例 10.7 !!float 示例

negative: !!float -1
zero: !!float 0
positive: !!float 2.3e4
infinity: !!float .inf
not a number: !!float .nan

10.2.2. 标签解析

JSON 模式标签解析故障安全模式标签解析的扩展。

所有具有“!”非特定标签的节点,按标准约定解析为“tag:yaml.org,2002:seq”、“tag:yaml.org,2002:map”或 “tag:yaml.org,2002:str”,具体取决于 其种类

具有“?”非特定标签的集合(即未标记集合)会根据其种类解析为“tag:yaml.org,2002:seq”或 “tag:yaml.org,2002:map”。

具有“?”非特定标签的标量(即普通标量)会 与一个正则表达式列表进行匹配(第一个匹配者胜出,例如 0 会 解析为 !!int)。 原则上,JSON 文件不应包含任何至少无法匹配其中一个表达式的标量。 因此,YAML 处理器应当将它们视为 错误。

正则表达式 解析为标签
null tag:yaml.org,2002:null
true | false tag:yaml.org,2002:bool
-? ( 0 | [1-9] [0-9]* ) tag:yaml.org,2002:int
-? ( 0 | [1-9] [0-9]* ) ( \. [0-9]* )? ( [eE] [-+]? [0-9]+ )? tag:yaml.org,2002:float
* 错误

注:float 的正则表达式并不完全匹配 JSON 规范中的表达式,在 JSON 规范中,小数点后至少需要一位数字:( \. [0-9]+ )。YAML 1.2 规范本意是匹配 JSON 行为,但 这无法在 1.2.2 规范中解决。

示例 10.8 JSON 标签解析

A null: null
Booleans: [ true, false ]
Integers: [ 0, -0, 3, -19 ]
Floats: [ 0., -0.0, 12e03, -2E+05 ]
Invalid: [ True, Null,
  0o7, 0x3A, +12.3 ]
{ "A null": null,
  "Booleans": [ true, false ],
  "Integers": [ 0, 0, 3, -19 ],
  "Floats": [ 0.0, -0.0, 12000, -200000 ],
  "Invalid": [ "True", "Null",
    "0o7", "0x3A", "+12.3" ] }

10.3. 核心模式

核心模式JSON 模式的扩展,允许以更 适合人类阅读的呈现方式表示相同类型。 这是推荐的默认模式,YAML 处理器应当使用它, 除非另有指示。 也强烈建议其他模式基于它。

10.3.1. 标签

核心模式使用与 JSON 模式相同的标签

10.3.2. 标签解析

核心模式标签解析JSON 模式标签 解析的扩展。

所有具有“!”非特定标签的节点,按标准约定解析为“tag:yaml.org,2002:seq”、“tag:yaml.org,2002:map”或 “tag:yaml.org,2002:str”,具体取决于 其种类

具有“?”非特定标签的集合(即未标记集合)会根据其种类解析为“tag:yaml.org,2002:seq”或 “tag:yaml.org,2002:map”。

具有“?”非特定标签的标量(即普通标量)会 与扩展后的正则表达式列表进行匹配。 但是,在这种情况下,如果没有任何正则表达式匹配,则标量 会被解析tag:yaml.org,2002:str(即 被视为字符串)。

正则表达式 解析为标签
null | Null | NULL | ~ tag:yaml.org,2002:null
/* Empty */ tag:yaml.org,2002:null
true | True | TRUE | false | False | FALSE tag:yaml.org,2002:bool
[-+]? [0-9]+ tag:yaml.org,2002:int(十进制)
0o [0-7]+ tag:yaml.org,2002:int(八进制)
0x [0-9a-fA-F]+ tag:yaml.org,2002:int(十六进制)
[-+]? ( \. [0-9]+ | [0-9]+ ( \. [0-9]* )? ) ( [eE] [-+]? [0-9]+ )? tag:yaml.org,2002:float(数字)
[-+]? ( \.inf | \.Inf | \.INF ) tag:yaml.org,2002:float(无穷)
\.nan | \.NaN | \.NAN tag:yaml.org,2002:float(非数)
* tag:yaml.org,2002:str(默认)

示例 10.9 核心标签解析

A null: null
Also a null: # Empty
Not a null: ""
Booleans: [ true, True, false, FALSE ]
Integers: [ 0, 0o7, 0x3A, -19 ]
Floats: [
  0., -0.0, .5, +12e03, -2E+05 ]
Also floats: [
  .inf, -.Inf, +.INF, .NAN ]
{ "A null": null,
  "Also a null": null,
  "Not a null": "",
  "Booleans": [ true, true, false, false ],
  "Integers": [ 0, 7, 58, -19 ],
  "Floats": [
    0.0, -0.0, 0.5, 12000, -200000 ],
  "Also floats": [
    Infinity, -Infinity, Infinity, NaN ] }

10.4. 其他模式

上述任何推荐模式都不排除使用任意显式 标签。 因此,特定编程语言的 YAML 处理器通常会提供 某种形式的本地标签,直接映射到该语言的原生数据 结构(例如 !ruby/object:Set)。

虽然此类本地标签对临时应用程序很有用,但它们 不足以支持稳定、可互操作的跨应用程序或 跨平台数据交换。

可互操作的模式会使用全局 标签(URI),这些标签在不同编程语言中表示 相同的数据。 此外,可互操作的模式可以提供额外的 标签解析 规则。 此类规则可以提供额外的正则表达式,也可以考虑通往节点的 路径。 这允许可互操作的模式使用未标记节点

强烈建议此类模式基于上文定义的 核心模式

参考链接