YAML 不是标记语言(YAML™)版本 1.2
修订版本 1.2.2 (2021-10-01)
版权目前由 YAML 语言开发团队所有 1
版权所有 2001-2009 由 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™(与“骆驼”押韵)是一种人性化、跨语言、基于 Unicode 的数据序列化语言,设计初衷是围绕动态编程语言的常见本地数据类型。它广泛用于从配置文件到互联网消息传递再到对象持久化以及数据审计和可视化等编程需求。与 Unicode 字符标准 4 一起,本规范提供了理解 YAML 版本 1.2 并创建处理 YAML 信息的程序所需的所有信息。
目录
第一章。YAML 简介
YAML(“YAML 不是标记语言”的递归缩写)是一种数据序列化语言,旨在与现代编程语言良好配合,以便进行日常常见任务。本规范既是对 YAML 语言及其支持概念的介绍,也是开发用于处理 YAML 的应用程序所需信息的完整规范。
开放、互操作和易于理解的工具极大地推动了计算机技术的发展。YAML 从一开始就被设计为对处理数据的人们有用且友好。它使用 Unicode 可打印字符,其中一些提供结构信息,其余包含数据本身。YAML 通过最小化结构字符的数量并允许数据以自然且有意义的方式展示自己,实现了独特的清晰度。例如,缩进可用于结构,冒号分隔键/值对,破折号用于创建“项目符号”列表。
有许多种数据结构,但它们都可以用三种基本原语充分表示:映射(哈希/字典)、序列(数组/列表)和标量(字符串/数字)。YAML 利用这些基本原语,并添加了一个简单的类型系统和别名机制,形成了一个完整的语言,用于序列化任何本地数据结构。虽然大多数编程语言都可以使用 YAML 进行数据序列化,但 YAML 在与那些基本围绕这三种基本原语构建的语言一起工作时表现出色。这些语言包括常见的动态语言,如 JavaScript、Perl、PHP、Python 和 Ruby。
编程语言有数百种,但用于存储和传输数据的语言却只有少数几种。尽管其潜力几乎是无限的,但 YAML 特别设计用于常见用例,如:配置文件、日志文件、进程间通信、跨语言数据共享、对象持久化和调试复杂数据结构。当数据易于查看和理解时,编程变得更简单。
1.1. 目标
YAML 的设计目标按优先级递减的顺序为:
- YAML 应该易于人类阅读。
- YAML 数据应该在不同编程语言之间可移植。
- YAML 应该与动态语言的本地数据结构匹配。
- YAML 应该具有一致的模型,以支持通用工具。
- YAML 应支持一遍处理。
- YAML 应该具有表现力和可扩展性。
- YAML 应该易于实现和使用。
1.2. YAML 历史
YAML 1.0 规范于 2004 年初由 Clark Evans、Oren Ben-Kiki 和 Ingy döt Net 在 yaml-core 邮件列表上经过 3 年的协作设计工作后发布 5 。该项目最初源自 Clark 和 Oren 在 SML-DEV 6 邮件列表上(用于简化 XML)以及 Ingy 为 Perl 开发的纯文本序列化模块 7 的工作。该语言从许多先前的技术和格式中汲取了许多灵感。
第一个 YAML 框架是在 2001 年用 Perl 编写的,Ruby 是第一种在其核心语言分发中作为 YAML 框架的语言于 2003 年发布的。
YAML 1.1 8 规范于 2005 年发布。在这个时候,开发人员开始意识到 JSON 9 。 几乎巧合的是,JSON 几乎是 YAML 的一个完整子集(在语法和语义上)。
2006 年,Kyrylo Simonov 制作了 PyYAML 和 LibYAML。许多不同编程语言中的 YAML 框架都是基于 LibYAML 构建的,许多其他人也将 PyYAML 视为他们实现的可靠参考。
YAML 1.2 3 规范于 2009 年发布。其主要重点是使 YAML 成为 JSON 的严格超集。它还删除了许多问题隐式类型推荐。
自 1.2 规范发布以来,YAML 的采用率持续增长,许多大型项目将其用作主要接口语言。2020 年,新的 YAML 语言设计团队开始定期会议,讨论改进 YAML 语言和规范的方式;以更好地满足其用户和使用案例的需求和期望。
这个 YAML 1.2.2 规范于 2021 年 10 月发布,是 YAML 复兴发展道路上的第一步。YAML 现在比以往任何时候都更受欢迎,但有一长串需要解决的问题,以使其充分发挥潜力。YAML 设计团队致力于使 YAML 尽可能好。
1.3. 术语
本文档中的关键词“必须”、“绝不能”、“需要”、“应当”、“不应”、“应该”、“不应该”、“建议”、“可以”和“可选”应按照 RFC 2119 中的描述进行解释 12 。
本文档的其余部分安排如下。第 2 章简要介绍了主要的 YAML 特性。第 3 章描述了 YAML 信息模型以及从该模型和 YAML 文本格式之间进行转换的过程。本文档的主体部分,第 4、5、6、7、8 和 9 章,正式定义了该文本格式。最后,第 10 章推荐了基本的 YAML 模式。
第 2 章 语言概述
本节快速展示了 YAML 的表现力。不要指望第一次阅读者能理解所有示例。相反,这些选择被用作规范其余部分的动机。
2.1. 集合
YAML 的块集合使用缩进来表示范围,并且每个条目都在自己的一行上开始。块序列使用短横线和空格(“ - ”)表示每个条目。映射使用冒号和空格(“ :”)来标记每个键/值对。注释以井号(也称为“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 一场比赛的逐步播报
--- 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 中,未标记的节点根据应用程序而被赋予类型。本规范中的示例通常使用故障安全模式中的 seq
、 map
和 str
类型。少数示例还使用 JSON 模式中的 int
、 float
和 null
类型。
示例 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.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 处理器不需要公开序列化或表示阶段。它可以直接在本机数据结构和字符流之间进行转换(如上图中的 dump 和 load)。但是,这样的直接转换应该发生在仅从表示中可用的信息构造本机数据结构的情况下。特别是,在构造过程中不应引用映射键顺序、注释和标记句柄。
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 方案。相比之下,本地标签特定于单个应用程序。本地标签以“ !
”开头,不是 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 章. 语法约定
以下章节正式定义了 YAML 字符流的语法,使用参数化的 BNF 产生式。每个 BNF 产生式都被命名和编号,以便轻松引用。在可能的情况下,基本结构在更复杂的结构之前以“自底向上”的方式指定。
这些产生式附带示例,以两栏并排的格式呈现。左侧是 YAML 示例,右侧是示例的另一种 YAML 视图。右侧视图在可能的情况下使用 JSON。否则,它使用尽可能接近 JSON 的 YAML 格式。
4.1. 产生式语法
使用语法 production-name ::= 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) )+ | … …
参数如下:
- 缩进:
n
或m
-
可以是任何自然数,包括零。
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-xDFFF
, xFFFE
和 xFFFF
。
在输入时,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
表示“非断 JSON 兼容”。
5.2. 字符编码
本规范中提到的所有字符都是 Unicode 代码点。每个这样的代码点根据所使用的字符编码写成一个或多个字节。请注意,在 UTF-16 中,大于 xFFFF
的字符被写成四个字节,使用代理对。
字符编码是一种展示细节,不得用于传达内容信息。
在输入时,YAML 处理器必须支持 UTF-8 和 UTF-16 字符编码。为了与 JSON 兼容,还必须支持 UTF-32 编码。
如果字符流以字节顺序标记开头,则字符编码将被视为字节顺序标记指示的编码。否则,流必须以 ASCII 字符开头。这允许通过空( x00
)字符的模式推断编码。
字节顺序标记可能出现在任何文档的开头,但是同一流中的所有文档必须使用相同的字符编码。
为了实现 JSON 兼容性,字节顺序标记也允许出现在带引号的标量内部。为了可读性,此类内容字节顺序标记在输出时应该被转义。
因此,可以通过将流的前几个字节与以下表行(按顺序)进行匹配来推断编码:
Byte0 | Byte1 | Byte2 | Byte3 | Encoding | |
---|---|---|---|---|---|
明确的 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 | |
Default | UTF-8 |
推荐的输出编码是 UTF-8。如果使用其他编码,建议使用明确的字节顺序标记,即使第一个流字符是 ASCII。
有关字节顺序标记和 Unicode 字符编码方案的更多信息,请参阅 Unicode FAQ 17 。
[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 |
|
" ,
" ( 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 } |
|
" #
"( x23
,八角形符号,井号,井号,井号,数字符号)表示注释。
[12] c-comment ::= '#'
" &
"( x26
,和号)表示节点的锚点属性。
[13] c-anchor ::= '&'
" *
" ( x2A
, 星号) 表示别名节点。
[14] c-alias ::= '*'
“ !
” ( x21
, 惊叹号) 用于指定节点标记。它用于表示标记指令和标记属性中使用的标记句柄;用于表示本地标记;以及作为非特定标记用于非纯量标量。
[15] c-tag ::= '!'
示例 5.6 节点属性指示符
anchored: !local &anchor value alias: *anchor |
|
" |
"( 7C
,竖线)表示一个文字块标量。
[16] c-literal ::= '|'
" >
"( x3E
,大于号)表示一个折叠块标量。
[17] c-folded ::= '>'
示例 5.7 块标量指示符
literal: | some text folded: > some text |
|
" '
"( x27
,撇号,单引号)包围一个单引号流量标量。
[18] c-single-quote ::= "'"
" "
"( x22
,双引号)包围一个双引号流量标量。
[19] c-double-quote ::= '"'
示例 5.8 引用标量指示符
single: 'text' double: "text" |
|
" %
"( x25
,百分比)表示指令行。
[20] c-directive ::= '%'
“ @
”( 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)↓ |
|
传说:
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"); ··} |
|
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 字母(字母)字符:
[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 空( x00
)字符。
[42] ns-esc-null ::= '0'
转义的 ASCII 响铃( 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 转义( 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" |
|
传说:
示例 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. |
|
传说:
s-indent(n)
Content
Neither content nor indentation
用于表示块集合条目的“ -
”、“ ?
”和“ :
”字符被人们认为是缩进的一部分。这由相关的制作逐案处理。
示例 6.2 缩进指示符
?·a :·-→b ··-··-→c ·····-·d |
|
传说:
Total Indentation
s-indent(n)
Indicator as indentation
6.2. 分隔空格
在缩进和标量内容之外,YAML 使用空白字符在行内标记之间进行分隔。请注意,这样的空白字符可以安全地包括制表符。
分隔空格是一种展示细节,不应用于传达内容信息。
[66] s-separate-in-line ::= s-white+ | <start-of-line>
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 |
|
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 · |
|
传说:
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.7 块折叠
> ··foo·↓ ·↓ ··→·bar↓ ↓ ··baz↓ |
|
传说:
b-l-folded(n,c)
Non-content spaces
Content spaces
- 流式折叠
-
在流式样式中进行折叠提供了更加宽松的语义。流式样式通常依赖于显式指示符而不是缩进来传达结构。因此,在一行文本之前或之后的空格是一种表现细节,不应用于传达内容信息。一旦所有这样的空格被丢弃,所有换行都会被折叠,没有例外。
-
流线折叠规则的综合效果是,每个“段落”被解释为一行,空行被解释为换行符,文本可以自由地进行更多缩进而不影响内容信息。
[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↓ " |
|
传说:
s-flow-folded(n)
Non-content spaces
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 |
|
在标量内容之外,注释可以单独出现在一行上,与缩进级别无关。请注意,在标量内容之外,只包含空白字符的行被视为注释行。
[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↓ ↓ |
|
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 |
|
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" |
|
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" |
|
为同一文档指定多个“ 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" |
|
在同一文档中为同一句柄指定多个 “ 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" |
|
- 次要句柄
-
次要标签句柄写作“
!!
”。这允许使用单个“次要”命名空间的紧凑表示法。默认情况下,与此句柄关联的前缀是“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
”指令与之关联了一些前缀,否则不能在标签速记中使用句柄名称。 -
句柄的名称是一种展示细节,不应用于传达内容信息。特别是,一旦解析完成,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" |
|
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 |
|
- 全局标记前缀
-
如果前缀以除“
!
”之外的字符开头,则必须是有效的 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 |
|
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+ '>'
- 标记缩写
-
标记缩写由有效的标记句柄后跟非空后缀组成。标记句柄必须与前缀关联,可以通过默认方式或使用“
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 |
|
示例 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.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 |
|
第 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 |
|
7.2. 空节点
YAML 允许在许多情况下省略节点内容。具有空内容的节点被解释为具有空值的普通标量。这些节点通常解析为“ null
”值。
[105] e-scalar ::= ""
在示例中,有时会将空标量显示为“ °
”符号,以便更清晰地显示。请注意,该符号对应于字符流中的位置,而不是实际字符。
节点的属性和节点内容都是可选的。这允许存在完全空的节点。只有在遵循某些明确指示其存在时,完全空节点才是有效的。
[106] e-node ::= e-scalar # ""
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, ] |
|
在多行双引号标量中,换行符受流行折叠的影响,会丢弃任何尾随的空白字符。也可以转义换行符。在这种情况下,转义的换行符不包括在内容中,而转义换行符之前的任何尾随空白字符都会被保留。结合转义空白字符的能力,这允许双引号行在任意位置中断。
[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" |
|
每行开头和结尾的所有空白字符都不包括在内容中。因此,每个连续行必须至少包含一个非空格字符。如果有空行,则将其作为行折叠的一部分消耗掉。
[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·" |
|
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
单引号标量在隐式键内部时被限制为单行。
[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, ] |
|
所有前导和尾随的空白字符都被排除在内容之外。因此,每个连续行必须至少包含一个非空格字符。如果有空行,则将其作为行折叠的一部分消耗。
[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·' |
|
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 ] |
|
传说:
ns-plain-first(c)
ns-plain-char(c)
Not ns-plain-first(c)
Not ns-plain-char(c)
当包含在隐式键内时,普通标量进一步限制为单行。
[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, ] |
|
所有前导和尾随的空白字符都被排除在内容之外。因此,每个连续行必须至少包含一个非空格字符。如果有空行,则将其作为行折叠的一部分消耗。
[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 |
|
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] |
|
任何流节点都可以用作流序列条目。此外,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, ] |
|
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} |
|
如果指定了可选的“ ?
”映射键指示器,则其余条目可能完全为空。
[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, ?°° } |
|
通常,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, } |
|
为了确保 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":° } |
|
如果映射包含单个键/值对,则在流序列内可以使用更紧凑的表示法。此表示法不需要周围的“ {
”和“ }
”字符。请注意,在这种情况下无法为映射指定任何节点属性。
如果“ ?
”指示符明确指定,则解析是明确的,并且语法与一般情况相同。
[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)
如果省略“ ?
”指示符,则解析需要查看隐式键之后才能识别它。为了限制所需的预读量,必须在键的开始位置之后最多出现 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 ] |
|
示例 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 |
|
完整的流节点还具有可选的节点属性,除了引用锚定节点属性的别名节点。
[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° |
|
第 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 |
|
8.1.1.1. 块缩进指示符
每个块标量都有一个内容缩进级别。块标量的内容不包括每行开头的一些空格,直到内容缩进级别为止。
如果块标量有一个缩进指示符,则块标量的内容缩进级别等于块标量的缩进级别加上缩进指示符字符的整数值。
如果没有给出缩进指示符,则内容缩进级别等于内容的第一行的前导空格数。如果没有非空行,则内容缩进级别等于最长行上的空格数。
如果任何非空行不以大于或等于内容缩进级别的空格开头,则会出现错误。
如果任何前导空行包含的空格比第一行非空行还多,则会出现错误。
YAML 处理器应仅在检测失败的情况下发出显式缩进指示符。
[163] c-indentation-indicator ::= [x31-x39] # 1-9
示例 8.2 块缩进指示符
- |° ·detected - >° · ·· ··# detected - |1 ··explicit - >° ·→ ·detected |
|
示例 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↓ |
|
块标量后面的尾随空行的解释也由块标量头部指定的切断指示符控制。
[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. |
|
如果块标量仅由空行组成,则这些行被视为尾随行,因此受到切断的影响。
示例 8.6 空标量吞噬
strip: >- ↓ clip: > ↓ keep: |+ ↓ |
|
8.1.2. 字面样式
字面样式由“ |
”指示符表示。这是最简单、最受限制和最可读的标量样式。
[170] c-l+literal(n) ::= c-literal # '|' c-b-block-header(t) l-literal-content(n+m,t)
在文本标量中,所有(缩进)字符都被视为内容,包括空白字符。请注意,所有换行字符都会被规范化。此外,空行不会被折叠,尽管最终换行符和尾随空行会被去除。
在文本标量中无法转义字符。这将限制它们为可打印字符。此外,无法换行长文本行。
[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 |
|
8.1.3. 折叠样式
折叠样式由“ >
”指示符表示。它类似于字面样式;但是,折叠标量会受到换行的影响。
[174] c-l+folded(n) ::= c-folded # '>' c-b-block-header(t) l-folded-content(n+m,t)
折叠允许长行在任何一个单个空格字符分隔两个非空格字符的地方断开。
[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 |
|
(以下三个示例重复此示例,每个示例突出显示不同的制作。)
以空格字符开头的行(更缩进的行)不会折叠。
[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 |
|
换行符和空行分隔折叠和更缩进的行也不会被折叠。
[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 |
|
传说:
b-as-line-feed
(separation) l-empty(n,c)
最后的换行符和尾随的空行(如果有的话)会被处理,并且永远不会被折叠。
[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 |
|
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↓ |
|
传说:
c-l-block-seq-entry(n)
auto-detected s-indent(n)
条目节点可以是完全空的,可以是嵌套的块节点,也可以使用紧凑的内联表示法。当条目本身是嵌套的块集合时,可以使用紧凑表示法。在这种情况下,“ -
”指示符和后面的空格都被视为嵌套集合的缩进的一部分。请注意,无法为这样的集合指定节点属性。
[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 |
|
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↓ |
|
传说:
ns-l-block-map-entry(n)
auto-detected s-indent(n)
如果指定了“ ?
”指示符,则必须在单独的一行上指定可选值节点,由“ :
”指示符表示。请注意,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↓ |
|
如果省略“ ?
”指示符,则解析需要查看隐式键之后的内容,就像在单个键/值对流映射中一样。因此,这些键受到相同的限制;它们仅限于单行,不能跨越超过 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 |
|
也可以使用紧凑的内联表示法。这种紧凑表示法可以嵌套在块序列和显式块映射条目中。请注意,无法为这样的嵌套映射指定节点属性。
[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↓ |
|
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↓ |
|
块节点的属性可能跨越多行。在这种情况下,它们的缩进必须比块集合多至少一个空格,而不管块集合条目的缩进如何。
[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 |
|
由于人们将“ -
”指示符视为缩进,嵌套块序列可能会缩进少一个空格以进行补偿,当然,除非嵌套在另一个块序列中([ 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 |
|
第 9 章 文档流产出
9.1. 文件
YAML 字符流可以包含多个文档。每个文档与其他文档完全独立。
9.1.1. 文档前缀
文档可以在前面加上指定字符编码和可选注释行的前缀。请注意,流中的所有文档必须使用相同的字符编码。但是,对于流中的每个文档,重新指定编码使用字节顺序标记是有效的。
可选前缀的存在并不一定表示实际文档的存在。
[202] l-document-prefix ::= c-byte-order-mark? l-comment*
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 |
|
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 |
|
传说:
9.1.4. 显式文档
明确文档以明确指令结束标记行开头,但没有指令。由于该标记指示了文档的存在,因此文档本身可能完全为空。
[208] l-explicit-document ::= c-directives-end ( l-bare-document | ( e-node # "" s-l-comments ) )
示例 9.4 明确文档
--- { matches % : 20 } ... --- # Empty ... |
|
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 ... |
|
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 |
|
如果作为一个整体,一系列字节符合上述 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 的哈希、Python 的字典和 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 的数组,Python 的列表或元组以及 Java 的数组或 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. 标签
除了 failsafe 模式定义的标签外,JSON 模式还使用以下标签:
10.2.1.1. 空值
- URI
-
tag:yaml.org,2002:null
- 种类
-
标量。
- 定义
-
表示缺少值。通常绑定到本机类似空值(例如,Perl 中的
undef
,Python 中的None
)。请注意,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 表示真,0 表示假。
- 规范形式
-
要么
true
,要么false
。
示例 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)仅提供“数字”类型,允许整数和浮点值。只要它们能够正确地往返,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)仅提供“数字”类型,允许整数和浮点值。只要它们能够正确地往返转换,YAML 处理器可以使用这种类型来表示浮点数。
-
并非所有浮点值都可以精确存储在任何给定的本机类型中。因此,当往返转换时,浮点值可能会发生“微小变化”。支持的范围和精度取决于实现,尽管 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 模式标签解析是 failsafe 模式标签解析的扩展。
所有带有“ !
”非特定标记的节点,按照标准约定,将被解析为“ 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]* ) |
标签:yaml.org,2002:int |
-? ( 0 | [1-9] [0-9]* ) ( \. [0-9]* )? ( [eE] [-+]? [0-9]+ )? |
标签:yaml.org,2002:float |
* |
Error |
注意:
float
的正则表达式与 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 ] |
|
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]+ |
标签:yaml.org,2002:整数(十进制) |
0o [0-7]+ |
标签:yaml.org,2002 年,int(基数 8) |
0x [0-9a-fA-F]+ |
标签:yaml.org,2002 年,int(基数 16) |
[-+]? ( \. [0-9]+ | [0-9]+ ( \. [0-9]* )? ) ( [eE] [-+]? [0-9]+ )? |
标签:yaml.org,2002 年,float(数字) |
[-+]? ( \.inf | \.Inf | \.INF ) |
标签:yaml.org,2002:浮点数(无穷大) |
\.nan | \.NaN | \.NAN |
标签:yaml.org,2002:浮点数(非数字) |
* |
标签:yaml.org,2002:字符串(默认) |
示例 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 ] |
|
10.4. 其他模式
以上推荐的模式都不排除使用任意显式标记。因此,特定编程语言的 YAML 处理器通常提供某种形式的本地标记,直接映射到语言的本机数据结构(例如, !ruby/object:Set
)。
尽管这些本地标记对于临时应用程序很有用,但对于稳定的、可互操作的跨应用程序或跨平台数据交换来说是不够的。
可互操作的模式利用代表不同编程语言中相同数据的全局标签(URI)。此外,可互操作的模式可能提供额外的标签解析规则。这些规则可能提供额外的正则表达式,并考虑到节点的路径。这使得可互操作的模式可以使用未标记的节点。
强烈建议这些模式基于上面定义的核心模式。
参考链接
-
YAML 语言开发团队 ↩
-
GitHub 上的 YAML 规范 ↩
-
YAML 不是标记语言(YAML™)版本 1.2 ↩ ↩ 2
-
Unicode - 文本和表情符号的世界标准 ↩
-
YAML 核心邮件列表 ↩
-
SML-DEV 邮件列表存档 ↩
-
Data::Denter - Data::Dumper 和 Storable 的(已弃用)替代方案 ↩
-
YAML 不是标记语言(YAML™)版本 1.1 ↩
-
JSON 数据交换语法 ↩
-
PyYAML - Python 的 YAML 解析器和发射器 ↩
-
LibYAML - 用于解析和发射 YAML 的 C 库 ↩
-
请求评论摘要 ↩
-
有向图 ↩
-
‘tag’ URI 方案 ↩
-
维基百科 - C0 和 C1 控制字符 ↩
-
维基百科 - 通用字符集字符 #代理项 ↩
-
UTF-8,UTF-16,UTF-32 和 BOM ↩
-
统一资源标识符(URI) ↩