YAML

人类可读的数据序列化格式

YAML/ˈjæməl/)是一个可读性高,用来表达资料序列化的格式。YAML参考了其他多种语言,包括:C语言PythonPerl,并从XML、电子邮件的数据格式(RFC 2822[1])中获得灵感。Clark Evans在2001年首次发表了这种语言[2],另外Ingy döt Net与Oren Ben-Kiki也是这语言的共同设计者[3]。目前已经有数种程式语言或脚本语言支援(或者说解析)这种语言。

YAML
扩展名
.yaml, .yml
互联网媒体类型
尚未注册
首次发布2001年5月11日,​23年前​(2001-05-11
最新版本
1.2 (Revision 1.2.2)
2021年10月1日,​3年前​(2021-10-01
格式类型数据交换
免费格式?
网站yaml.org

YAML是"YAML Ain't a Markup Language"(YAML不是一种标记语言)的递回缩写。在开发的这种语言时,YAML的意思其实是:"Yet Another Markup Language"(仍是一种标记语言[4],但为了强调这种语言以数据为中心,而不是以标记语言为重点,而用反向缩略语重新命名。

功能

YAML的语法和其他高阶语言类似,并且可以简单表达清单、散列表,纯量等资料形态。[5]它使用空白符号缩排和大量依赖外观的特色,特别适合用来表达或编辑资料结构、各种设定档、倾印除错内容、文件大纲(例如:许多电子邮件标题格式和YAML非常接近)。尽管它比较适合用来表达阶层式(hierarchical model)的资料结构,不过也有精致的语法可以表示关联性(relational model)的资料。[6]由于YAML使用空白字元和分行来分隔资料,使得它特别适合用grepPythonPerlRuby操作。其让人最容易上手的特色是巧妙避开各种封闭符号,如:引号、各种括号等,这些符号在巢状结构时会变得复杂而难以辨认。

范例

简单的文件

资料结构可以用类似大纲的缩排方式呈现

---
receipt:     Oz-Ware Purchase Invoice
date:        2012-08-06
customer:
    given:   Dorothy
    family:  Gale
   
items:
    - part_no:   A4786
      descrip:   Water Bucket (Filled)
      price:     1.47
      quantity:  4

    - part_no:   E1628
      descrip:   High Heeled "Ruby" Slippers
      size:      8
      price:     133.7
      quantity:  1

bill-to:  &id001
    street: | 
            123 Tornado Alley
            Suite 16
    city:   East Centerville
    state:  KS

ship-to:  *id001   

specialDelivery:  >
    Follow the Yellow Brick
    Road to the Emerald City.
    Pay no attention to the
    man behind the curtain.
...

注意在YAML中,字串不一定要用双引号标示。另外,在缩排中空白字元的数目并不是非常重要,只要相同阶层的元素左侧对齐就可以了(不过不能使用TAB字元)。这个文件的顶层由七个键值组成:其中一个键值"items",是两个元素构成的阵列(或称清单),这清单中的两个元素同时也是包含了四个键值的杂凑表。文件中重复的部分用这个方法处理:使用锚点(&)和引用(*)标签将"bill-to"杂凑表的内容复制到"ship-to"杂凑表。也可以在文件中加入选择性的空行,以增加可读性。在一个档案中,可同时包含多个文件,并用"---"分隔。选择性的符号"..."可以用来表示档案结尾(在利用串流的通讯中,这非常有用,可以在不关闭串流的情况下,发送结束讯号)。

语言的构成元素

YAML的基本元件

YAML提供缩排/区块以及内置(inline)两种格式,来表示清单和杂凑表。以下展示几种YAML的基本元件。

清单(阵列

习惯上清单比较常用区块格式(block format)表示,也就是用短杠+空白字元作为起始。

 --- # 最喜愛的電影
 - Casablanca
 - North by Northwest
 - Notorious

另外还有一种内置格式(inline format)可以选择──用方括号围住,并用逗号+空白区隔(类似JSON的语法)。[7]

 --- # 購物清單 
 [milk, pumpkin pie, eggs, juice]

键值和资料由冒号及空白字元分开。区块形式(常使用于YAML数据文档中)使用缩进和换行符分隔key: value对。内置形式(常使用于YAML数据串流中)在大括号中使用逗号+空白字元分隔key: value对。

 --- # 區塊形式
   name: John Smith
   age: 33
 --- # 內置形式
 {name: John Smith, age: 33}

区块的字符

再次强调,字串不需要包在引号之内。

有两种语法可以书写多行文字(multi-line strings),一种为保留换行(使用|字元),另一种为折叠换行(使用>字元)。两种语法在其特殊字元之后都必须接著换行。

保留换行(Newlines preserved)
data: |                                     # 譯者注:這是一首著名的五行民謠(limerick)
   There once was a man from Darjeeling     # 這裡曾有一個人來自大吉嶺
   Who got on a bus bound for Ealing        # 他搭上一班往伊靈的公車
       It said on the door                  # 門上這麼說的
       "Please don't spit on the floor"     # "請勿在地上吐痰"
   So he carefully spat on the ceiling      # 所以他小心翼翼的吐在天花板上

根据预设,每行开头的缩排(以首行为基准)和行末空白会被去除,而不同的缩排会保留差异。

Chomping Final Line Break

使用保留换行时,作为最后一行结尾的换行(\n)会包含在文本之内,若希望使用此缩排方式,但不想要含有最后的换行,可以使用Chomping Final Line Break。

data: |-                                     # 譯者注:這是一首著名的五行民謠(limerick)
   There once was a man from Darjeeling     # 這裡曾有一個人來自大吉嶺
   Who got on a bus bound for Ealing        # 他搭上一班往伊靈的公車
       It said on the door                  # 門上這麼說的
       "Please don't spit on the floor"     # "請勿在地上吐痰"
   So he carefully spat on the ceiling      # 所以他小心翼翼的吐在天花板上
折叠换行(Newlines folded)
data: >
   Wrapped text         # 摺疊的文字
   will be folded       # 將會被收
   into a single        # 進單一一個
   paragraph            # 段落
   
   Blank lines denote   # 空白的行代表
   paragraph breaks     # 段落之間的區隔

和保留换行不同的是,只有空白行才视为换行,原本的换行字元会被转换成空白字元,而行首缩排会被去除。

阶层化的元素

于清单中使用杂凑表
- {name: John Smith, age: 33}
- name: Mary Smith
  age: 27
于杂凑表中使用清单
men: [John Smith, Bill Jones]
women:
  - Mary Smith
  - Susan Williams

YAML的进阶元件

这部分算是一个后续的讨论,在比较各种数资料列语言时,YAML最常被提到的特色有两个:关系树和资料形态。

树状结构之间的交互参照

资料合并和参考

为了维持文件的简洁,并避免资料输入的错误,YAML提供了节点参考(*)和杂凑合并(<<)参考到其他节点标签的锚点标记(&)。参考会将树状结构加入锚点标记的内容,并可以在所有资料结构中运作(可以参考上面"ship-to"的范例)合并只有杂凑表可以使用,可以将键值自锚点标记复制到指定的杂凑表中。

当资料被instantiate合并和参考会被剖析器自动展开。

#眼部雷射手術之標準程序
---
- step:  &id001                  # 定義錨點標籤 &id001
    instrument:      Lasik 2000
    pulseEnergy:     5.4
    pulseDuration:   12
    repetition:      1000
    spotSize:        1mm

- step:
     <<: *id001                  # 合併鍵值:使用在錨點標籤定義的內容
     spotSize:       2mm         # 覆寫"spotSize"鍵值

- step:
     <<: *id001                  # 合併鍵值:使用在錨點標籤定義的內容
     pulseEnergy:    500.0       # 覆寫鍵值
     alert: >                    # 加入其他鍵值
           warn patient of 
           audible pop

资料形态

由于自动判定资料形态的功能,严格型态(也就是使用者有宣告的资料形态)很难在大部分的YAML文件中看到。资料型态可以被区分成三大类:原码(core),定义(defined),使用者定义(user-defined)。原码可以自动被解析器分析(例如:浮点数,整数,字串,清单,映射,...)。有一些进阶的资料形态──例如位元资料──在YAML中有被“定义”,但不是每一种解析器都有支援。最后,YAML支援使用者自定的区域变数,包括:自订的类别,结构或基本型态(例如:四倍精度的浮点数)。

强迫转型

YAML的自动判定资料形态是哪一种实体。但有时使用者会想要将资料强迫转型成自定的某种型态。最常见的状况是字串,有时候可能看起来像数字或布林值,这种时候可以使用双引号,或是使用严格型态标签。

---
a: 123                     # 整數
b: "123"                   # 字串(使用雙引號)
c: 123.0                   # 浮點數
d: !!float 123             # 浮點數,使用!!表達的嚴格型態
e: !!str 123               # 字串,使用嚴格型態
f: !!str Yes               # 字串,使用嚴格型態
g: Yes                     # 布林值"真"(yaml1.1)或字串"Yes"(yaml1.2)
h: Yes we have No bananas  # 字串(包含"Yes"和"No")
其他特殊资料形态

除了一般的资料形态之外,使用者也可以使用一些较为进阶的型态,但不保证可被每种解析器分析。使用时和强迫转型类似,要在形态名称之前加上两个惊叹号(!!)。有几种重要的形态在本篇没有讨论,包括集合(sets),有序映照(ordered maps),时间戳记(timestamps)以及十六进位资料(hexadecimal)。下面这个范例则是位元资料(binary)

---
picture: !!binary |
 R0lGODdhDQAIAIAAAAAAANn
 Z2SwAAAAADQAIAAACF4SDGQ
 ar3xxbJ9p0qa7R0YxwzaFME
 1IAADs=
使用者自行扩充的资料形态

许多YAML的实现允许使用者自订资料形态。在将一个物件序列化时,这个方法还颇方便的。某些区域资料形态可能不存在预设的资料形态中,不过这种型态在特定的YAML应用程式中是有定义的。这种区域资料形态用惊叹号( ! )表示。

---
myObject:  !myClass { name: Joe, age: 15}

语法

在yaml.org[8](英文)可以找到轻巧而好用的小抄[9](亦是用YAML表示)及格式说明[10]。下面的内容,是关于基本元件的摘要。

  • YAML使用可列印的Unicode字元,可使用UTF-8UTF-16
  • 使用空白字元为文件缩排来表示结构;不过不能使用跳格字元(TAB)。
  • 注解由井字号( # )开始,可以出现在一行中的任何位置,而且范围只有一行(也就是一般所谓的单行注解)
  • 每个清单成员以单行表示,并用短杠+空白( -   )起始。或使用方括号 [ ] ),并用逗号+空白( ,   )分开成员。
  • 每个杂凑表的成员用冒号+空白( :   )分开键值和内容。或使用大括号( {   } ),并用逗号+空白( ,   )分开。
    • 杂凑表的键值可以用问号 ( ? )起始,用来明确的表示多个词汇组成的键值。
  • 字串平常并不使用引号,但必要的时候可以用双引号 ( " )或单引号 ( ' )框住。
    • 使用双引号表示字串时,可用倒斜线( \ )开始的跳脱字元(这跟C语言类似)表示特殊字元。
  • 区块的字串用缩排和修饰词(非必要)来和其他资料分隔,有新行保留(preserve)(使用符号 | )或新行折叠(flod)(使用符号 > )两种方式。
  • 在单一档案中,可用连续三个连字号---)区分多个档案。
    • 另外,还有选择性的连续三个点号( ... )用来表示档案结尾。
  • 重复的内容可使从参考标记星号 ( * )复制到锚点标记( & )。
  • 指定格式可以使用两个惊叹号 ( !! ),后面接上名称。
  • 档案中的单一文件可以使用指导指令,使用方法是百分比符号( % )。有两个指导指令在YAML1.1版中被定义:
    • %YAML 指导指令,用来识别文件的YAML版本。
    • %TAG 指导指令,被用在URI的字首标记。这个方法在标记节点的型态时相当有用。

YAML在使用逗号及冒号时,后面都必须接一个空白字元,所以可以在字串或数值中自由加入分隔符号(例如:5,280或http://www.wikipedia.org)而不需要使用引号。

另外还有两个特殊符号在YAML中被保留,有可能在未来的版本被使用--( @ )和( ` )。

与其他数据序列化格式比较

虽然YAML是参考JSONXMLSDL等语言,不过跟这些语言比起来,YAML仍有自己的特色。

JSON

JSON的语法是YAML1.2版的子集[11],,同时非常接近[12] YAML1.0与1.1版的子集,因此大部分的JSON文件都可以被YAML的剖析器剖析。[13]这是因为JSON的语法结构和YAML的内置格式相同。虽然大范围的分层也可以使用类似JSON的内置格式,不过YAML标准并不建议这样使用,除非这样编写能让文件可读性增加。YAML的许多扩展在JSON是找不到的,如:进阶资料形态、关系锚点、字串不需要双引号、映射资料形态会储存键值的顺序。

XML和SDL

XML和SDL标签概念,在YAML中是找不到的。对于资料结构序列(尽管这是有争议的),标签属性的特色就是可以将资料及复杂资料附加资讯分离,并将各种原生资料结构(如:杂凑表、阵列)用同一种语言表示。YAML则以资料的可扩展性作为替代。(包括为了模拟物件的类别型态)在YAML本身的规范中,并没有类似XML的语言定义文件刚要(language-defined document schema descriptors)──例如验证自己本身的结构是否正确的文件。不过,YAML纲要描述语言(YAML schema descriptor language)是存在[14]的。另外还有YAXML──用XML描述YAML的结构──可以让XML SchemaXSLT转换程式应用在YAML之上。况且,在一般使用的情况下,YAML丰富的定义型态之语法已经提供了足够的方式,来辨认YAML文件是否正确。

缩进划界

由于YAML的运作主要依赖大纲式的缩进来决定结构,这有效解决了界定符冲突(Delimiter collision)的问题。YAML的资料形态不依赖引号之特点,使的YAML文件可以利用区块,轻易的插入各种其他类型文件,如:XML、SDL、JSON,甚至插入另一篇YAML。

---
example: >
        HTML goes into YAML without modification
message: |

        <blockquote style="font: italic 12pt Times">
        <p>"Three is always greater than two,
           even for large values of two"</p>
        <p>--Author Unknown</p>
        </blockquote>

相反的,要将YAML置入XML或SDL中时,需要将所有空白字元和位势符号(potential sigils,如:<,>&)转换成实体语法;要将YAML置入JSON中,需要用引号框住,并转换内部的所有引号。

非阶层式的资料模型

跟SDL、JSON等,每个子结点只能有单一一个父节点的阶层式模型不同,YAML提供了一个简单的关系体制,可以从树状结构的其他地方,重复相同的资料,而不必显示那些冗馀的结构。这点和XML中的IDRef类似[15]YAML剖析器在将YAML转换成物件时,会自动将那些参考资料的结构展开,所以程式在使用时并不会查觉到哪些资料是解码自这种结构。XML则不会将这种结构展开。这种表示法可以增加程式的可读性,并且,在那种‘大部分参数维持和上次相同,只有少数改变’的设定档及通讯协定中,可以减少数据输入错误。一个例子是:‘送货地点’和‘购买地点’在发票的纪录中几乎都是相同的资料。

实际的考量

YAML是“行导向的”,因此,就算想由现有程序的混乱输出,转换成YAML格式,并保留大部分的原始文件之外观,也非常简单。因为他不需要平衡封闭的标签、括弧及引号,可以从很简单的利用程式,从报表产生YAML。同样,空格分隔可让使用行导向的命令如:grep、Awk、perl、ruby,和Python,来应急性的过滤YAML文件时更加方便。

特别是与标记语言不同的,连续的YAML区块导向往往是格式良好的YAML文件本身。这使得很容易撰写那种“在开始提取的具体记录之前,不需要'读取全部文件内容'”的解析器(通常需要平衡起始和关闭标签、寻找引号和跳脱字符)。当处理一个单一静态的,整个存在记忆体中的资料结构将很大,或为提取一个项目来重建的整个结构,代价相当昂贵的记录档,这种特性是相当方便的。

值得讨论的是,尽管它的缩排方式似乎复杂化了深度很大的巢状层次, YAML将缩排视为一个单一的空白,这可能会取得比其他标记语言更好的压缩比。此外,极深的缩排可以完全避免的是: (1)使用“内置格式”(即简称类JSON格式)而无缩排;或 (2)使用关联锚点展开阶层以形成一个摊平的格式,使得YAML解析器能透明地重组成完整的数据结构。

安全性

YAML是纯粹用来表达资料的语言,所以内部不会存代码注射的可执行命令。[16]这代表剖析器会相当(至少)安全的解析文件,而不用担心潜在与执行命令相关的安全漏洞。举例来说,JSONJavaScript的子集,因此可能有人想使用JavaScript本身的剖析器直接eval,不过这样一来就造成许多代码注射的漏洞。虽然在所有资料序列语言中,安全解析本质上是可能的,但可执行性却正是这样一个恶名昭彰的缺陷;而YAML缺乏相关的命令语言,可能是一个相对安全的利益。

资料处理和呈现

XML[17][18]和YAML规范[19]提供非常不同的逻辑模型来进行资料结点的展现、处理及储存。

实作与函式库

移植性

简单的YAML档案(例如:简单的键值对)不需要完整的YAML剖析器,便可以被正则表达式解析。许多常用的程式语言──纯用某个语言,让函式库具有可携性──都有的YAML的产生器和剖析器。当效能比较重要时,也有许多和C语言绑定的函式库可使用。

C语言函式库

  • libYAML[20] 2007-06时,这个YAML的函式库渐趋稳定,并被YAML格式作者推荐[21]
  • SYCK[22] 这个实现支援大部分1.0版的格式,并且被广泛的使用。它使用高阶解释型语言进行最佳化。在2005之后,这个专案已经不再更新,不过仍可使用。

其他函式库

下面几种程式语言都有与YAML相关的函式库:

  • C++
    • 用C++将libYaml包装[23]
  • Go
    • go-yaml[24] 借鉴 C 库 libyaml 设计的 Go 语言移植。
  • Haskell
    • Haskell Reference wrappers[25]
  • Java
    • jvyaml[26] 以Syck为基础设计。
    • JYaml[27] 纯Java的实现。
  • JavaScript
    • 原生的JavaScript即可产生YAML,但不能解析。
    • YAML JavaScript[28] 同时支持产生和解析。
  • Objective-C
  • OCaml
  • Lua
  • Perl
    • CPAN YAML[32] 一个通用的介面,被数个YAML解析器使用。
    • YAML::Tiny[33] YAML简化版的实现。拥有小巧轻快的优点──比完整功能的YAML实现快上许多,并用纯Perl写成。
    • YAML::Syck[34] 与SYCK函式库绑定。提供快速、功能丰富(highly featured)的YAML解析器。
    • YAML::XS[35] 与LibYaml绑定。提供1.1版更好的相容性。
  • PHP
    • Spyc[36] 纯PHP的实现。
    • PHP-Syck[37](与SYCK函式库绑定)
    • sfYaml[38]symfony项目重写的Spyc, 可独立使用,可以产生和解析YAML文件。
  • Python
    • PyYaml[39] 纯Python,或可选用LibYAML的函式库。
    • PySyck[40] 与SYCK绑定。
  • Ruby(从1.8版开始,YAML解析器成为标准函式库之一。以SYCK为基础。)
  • R
    • CRAN YAML[42] 以SYCK为基础。
  • .NET
  • Scala
  • Tcl
    • 在Tcl 8.4中可用[46]
  • XML YAXML[47] (当前仍在设计中)

常见错误与使用细节

  • 编辑器:
    • 建议使用能将跳格字元自动转换成空白字元的编辑器,并且使用定宽度的字型。
    • 编辑器要能正确的处理UTF-8和UTF16编码(或是使用纯ASCII编码──它同时是UTF-8的子集).
  • 字串:
    • YAML的字串不需使用引号,这可以增加可读性,并避免巢状的跳脱字元。然而,这有时也会导致错误,例如,字串本身是一个暧昧的字眼(像数字或布林值);或在短句中意外的出现YAML的结构符号(常见的例子是由惊叹号起始的句子,或是包含冒号-空白的句子:"!Caca de vaca!"、"Caution: lions ahead")。这在发布YAML档案时并不造成困扰,但在制作小型指令码和人工编辑档案时,这问题却会常常出现。比较好的方法是善用区块符号("|" or ">")而不要使用单行字串,来避免这种暧昧的表示方法。
  • 预期实做的特性

参看

其他标记语言:

注释

  1. ^ 2822页面存档备份,存于互联网档案馆
  2. ^ Evans, Clark. YAML Draft 0.1. Yahoo! Tech groups(雅虎技术群组): sml-dev. 2001-05-11 [2008-08-02]. (原始内容存档于2013-09-07). 
  3. ^ YAML Ain't Markup Language: About. [2010-06-16]. (原始内容存档于2022-04-03). 
  4. ^ Yet Another Markup Language (YAML) 1.0. [2008-11-24]. (原始内容存档于2022-03-06). 
  5. ^ 在英文原文中,将下列几组词汇交替使用:(清单"list"和阵列"array"), (杂凑表"hash"、字典"dictionary"、映射"mapping")和(字串"string"和纯量"scalar")。在其他程式语言,这几组字的意义未必等价。中文则统一使用相同的词汇。
  6. ^ 阶层式(hierarchical model)对应到树状结构时,只会有一个固定、整体性的结果。举例说明:用阶层式来表达电影的演员名单,每个演员都只能归类到一个电影名称;或是每个电影只能归类给一位演员(虽然这样很怪)。YAML同时也允许使用关联式资料(relational model)。
  7. ^ 存档副本. [2013-05-15]. (原始内容存档于2008-09-14). 
  8. ^ yaml.org页面存档备份,存于互联网档案馆
  9. ^ 小抄页面存档备份,存于互联网档案馆
  10. ^ 格式说明页面存档备份,存于互联网档案馆
  11. ^ YAML 1.2 Spec. [2008-12-01]. (原始内容存档于2008-05-16). 
  12. ^ 这些语法非常细微且很少出现在文件中:JSON允许扩充字元集,如:UTF32;YAML在分隔符号──如引号、等号、冒号──后方需要接空白字元,而JSON不需要;一些非标准的JSON实现允许Javascript风格的的大范围注解/*...*/。处理这种边界情形或许需要在将其当成内置格式的YAML剖析前进行简单的JSON前处理。
  13. ^ 用SYCK剖析JSON. [2008-09-28]. (原始内容存档于2008-09-14). 
  14. ^ 存在页面存档备份,存于互联网档案馆
  15. ^ XML IDREF. [2008-09-28]. (原始内容存档于2022-05-15). 
  16. ^ 提案中的"yield"(迭代器)标签可以允许简单的算术运算
  17. ^ Extensible Markup Language (XML) 1.0 (第四版). [2007-11-04]. (原始内容存档于2020-01-10). 
  18. ^ Extensible Markup Language (XML) 1.1 (第二版). [2007-11-04]. (原始内容存档于2021-04-19). 
  19. ^ YAML Ain't Markup Language (YAML) Version 1.1. [2007-11-04]. (原始内容存档于2022-05-01). 
  20. ^ libYAML页面存档备份,存于互联网档案馆
  21. ^ YAML的创造者Clark Evans推荐
  22. ^ SYCK页面存档备份,存于互联网档案馆
  23. ^ 用C++将libYaml包装[永久失效链接]
  24. ^ go-yaml页面存档备份,存于互联网档案馆
  25. ^ Haskell Reference wrappers
  26. ^ jvyaml
  27. ^ JYaml
  28. ^ YAML JavaScript页面存档备份,存于互联网档案馆
  29. ^ Cocoa-Syck
  30. ^ OCaml-Syck页面存档备份,存于互联网档案馆
  31. ^ Lua-Syck
  32. ^ CPAN YAML页面存档备份,存于互联网档案馆
  33. ^ YAML::Tiny页面存档备份,存于互联网档案馆
  34. ^ YAML::Syck页面存档备份,存于互联网档案馆
  35. ^ YAML::XS页面存档备份,存于互联网档案馆
  36. ^ Spyc
  37. ^ PHP-Syck页面存档备份,存于互联网档案馆
  38. ^ sfYaml
  39. ^ PyYaml. [2008-09-28]. (原始内容存档于2008-09-17). 
  40. ^ PySyck. [2022-06-03]. (原始内容存档于2021-05-11). 
  41. ^ Ya2YAML
  42. ^ CRAN YAML页面存档备份,存于互联网档案馆
  43. ^ YAML .NET Parser页面存档备份,存于互联网档案馆
  44. ^ YamlDotNet 12.0.0. www.nuget.org. [2022-09-14]. (原始内容存档于2022-09-16) (英语). 
  45. ^ scala-yaml页面存档备份,存于互联网档案馆
  46. ^ 在Tcl 8.4中可用
  47. ^ YAXML页面存档备份,存于互联网档案馆

外部链接