Skip to content

YAML – 信息模型

为了有效地使用和理解 YAML,了解其底层信息模型(information model)会很有帮助。当一个 YAML 文档被处理时,它会被转换成一个抽象数据结构,通常称为“表示图(representation graph)”或“节点图(node graph)”。应用程序与这个图交互,而不是直接与原始的 YAML 文本交互。

YAML 中原生应用程序数据的表示被建模为一个带有标签节点的、有根、连接的、有向图(rooted, connected, directed graph of tagged nodes)。我们来分解一下:

  • 图(Graph):节点的集合(数据项)和边(节点之间的关系)。
  • 有向(Directed):关系有方向(例如,一个映射节点指向其键节点和值节点)。
  • 有根(Rooted):图有一个单一的起始点(YAML 文档的根节点)。
  • 连接(Connected):所有节点都可以从根节点到达。
  • 带有标签的节点(Tagged Nodes):图中的每个节点都有一个标签(tag),指定其数据类型(例如,字符串、整数、序列、映射)。

整个转换过程可以视为:

应用程序数据 ⇔ YAML 节点图(标量、序列、带有标签的映射)⇔ YAML 文本表示

YAML 节点图主要由三种节点组成:

表示单一的原子数据值(atomic data values)。其内容是 Unicode 字符序列。示例包括字符串、数字、布尔值、日期和 null 值。每个标量节点都有一个标签(tag)(例如 !!str、!!int、!!bool、!!null、!!timestamp),用于定义如何解释其字符内容。

表示零个或多个其他节点的有序系列(ordered series)。这对应于编程语言中的列表(lists)或数组(arrays)。序列节点包含一个有序的子节点列表。其典型标签是 !!seq。

表示键值对(key-value pairs)的无序关联。这对应于字典(dictionaries)、对象(objects)或哈希映射(hash maps)。映射节点包含一组对,每对由一个键节点(key node)和一个值节点(value node)组成。映射中的键必须是唯一的。映射节点的典型标签是 !!map。虽然键通常是标量节点,但 YAML 允许键是复杂节点(序列或映射本身)。

序列和映射统称为“集合节点(collection nodes)”。

YAML 图中的每个节点都有一个标签(tag)。标签至关重要,因为它们决定了节点内容的解释方式。虽然 YAML 通常根据语法推断标签(例如,123 被推断为 !!int,true 被推断为 !!bool),但标签也可以显式指定:

  • 全局标签(缩写):!!tagname(例如,!!str,!!map)。这些是标准 YAML 标签集的一部分。
  • 主标签(应用特定或本地):!tagname(例如,!person,!config-setting)。这些可以由应用程序定义。
  • 次级标签(!<prefix>!tagname):用于更复杂的基于 URI 的标签,在日常使用中不太常见。

节点图结构示例:

# YAML 文档
user_profile: # 根节点,隐式为映射 (!!map)
name: Alice # 标量节点 (!!str),'user_profile' 的子节点
age: 30 # 标量节点 (!!int),'user_profile' 的子节点
hobbies: # 序列节点 (!!seq),'user_profile' 的子节点
- reading # 标量节点 (!!str),'hobbies' 的子节点
- coding # 标量节点 (!!str),'hobbies' 的子节点

这段 YAML 在内部会被表示为一个图,其中 user_profile 是一个映射节点。这个映射节点有三个子条目:一个用于 name(键),指向 Alice(值,一个标量字符串节点);一个用于 age(键),指向 30(值,一个标量整数节点);还有一个用于 hobbies(键),指向一个序列节点。hobbies 序列节点则有两个子标量字符串节点:reading 和 coding。

锚点和别名:创建图(不仅仅是树)

Section titled “锚点和别名:创建图(不仅仅是树)”

YAML 的锚点(anchor)(&)和别名(alias)(*)机制允许在文档中多次引用同一个节点。这意味着表示图确实可以是一个具有循环或共享子结构的普通图,而不仅仅是一个简单的树。当遇到别名时,它指向由锚点标记的同一个节点实例。

default_address: &addr
street: 123 Main St
city: Anytown
billing_address: *addr
shipping_address: *addr

在这种情况下,billing_address 和 shipping_address 都指向由 &addr 定义的完全相同的映射节点实例。

  • 可移植性(Portability):它提供了一种跨不同编程语言和环境思考 YAML 数据的通用方式。
  • 理解 YAML 库(Libraries):它阐明了 YAML 解析器(parsers)和序列化器(serializers)的内部工作原理。
  • 高级数据结构化:它有助于使用锚点、别名和自定义标签等特性设计复杂的数据关系。
  • 调试:理解预期的节点结构有助于诊断 YAML 解析或生成中的问题。

对于那些对最正式细节感兴趣的人,官方 YAML 规范(可在 yaml.org 获取)提供了对其信息模型和处理规则的深入描述。