YAML
2010-09-06 09:56:53 阿炯

YAML(IPA: /ˈjæməl/,尾音类似camel骆驼)是一个可读性高,用来表达资料序列的编程语言。YAML参考了其他多种语言,包括:XML、C语言、Perl以及电子邮件格式RFC2822。Clark Evans在2001年在首次发表了这种语言,另外Ingy döt Net与Oren Ben-Kiki也是这语言的共同设计者,目前已经有数种编程语言或脚本语言支持(或解析)这种语言。

YAML是"YAML Ain't a Markup Language"(YAML不是一种置标语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种置标语言),但为了强调这种语言以数据做为中心,而不是以置标语言为重点,而用返璞词重新命名。

功能

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

范例
简单的文件
数据结构可以用类似大纲的缩排方式呈现
---
receipt:  Oz-Ware Purchase Invoice
date:  2007-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
price:  100.27
quantity:  1

bill-to:  &id001
street: |
123 Tornado Alley
Suite 16
city:   East Westville
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"杂凑表。也可以在文件中加入选择性的空行,以增加可读性。在一个档案中,可同时包含多个文件,并用"——"分隔。选择性的符号"..."可以用来表示档案结尾(在利用串流的通讯中,这非常有用,可以在不关闭串流的情况下,发送结束号)。总结如下:

字串不一定要用双引号标识;

在缩排中空白字符的数目并不是非常重要,只要相同阶层的元素左侧对齐就可以了(不过不能使用TAB字符);

允许在文件中加入选择性的空行,以增加可读性;

在一个档案中,可同时包含多个文件,并用“——”分隔;

选择性的符号“...”可以用来表示档案结尾(在利用串流的通讯中,这非常有用,可以在不关闭串流的情况下,发送结束讯号)。


语言的构成元素

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

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

--- # 最喜爱的电影
- Casablanca
- North by Northwest
- Notorious

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

--- # 购物清单
[milk, pumpkin pie, eggs, juice]

杂凑表

键值和资料由冒号及空白字符分开。

--- # 区块形式
name: John Smith
age: 33
--- # 内置形式
{name: John Smith, age: 33}

区块的字符

再次强调,字串不需要包在引号之内。
保存新行(Newlines preserved)

--- | #译者注:这是一首著名的五行民谣(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   #所以他小心翼翼的吐在天花板上

根据设定,前方的引领空白符号(leading white space)必须排成条状,以便和其他资料或是行为(如范例中的缩排)明显区分。

折叠新行(Newlines folded)

--- >
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也有用来描述好几行相同结构的数据的缩写语法,数组用'[]'包括起来,hash用'{}'来包括。因此能够缩写成这样:
house:
family: { name: Doe, parents: [John, Jane], children: [Paul, Mark, Simone] }
address: { number: 34, street: Main Street, city: Nowheretown, zipcode: 12345 }

树状结沟之间的交互参照

资料合并和参考
为了维持文件的简洁,并避免资料输入的错误,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 # 布林值"真"
h: Yes we have No bananas  # 字串(包含"Yes"和"No")

其他特殊资料形态

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

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

使用者自行扩充的资料形态

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

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

语法

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

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

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

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


YAML 格式规则注意

规则一:缩近

YAML 使用一个固定的缩进风格表示数据层结构关系,需要每个缩进级别由两个空格组成,注意不要使用 tabs 键。例如 mysql.yaml 中 options 和 replication 这是2个层级,用2个空格来表示,如果同一层级,那就竖直对齐就好啦:

init_config:

instances:
  - server: freeoa
    user: oneapm
    pass: YourPassword
    tags:
      - tag_key1:tag_value1
      - tag_key2:tag_value2
    options:
      replication: 0
      galera_cluster: 1

规则二:短横杠

想要表示列表项,使用一个短横杠加一个空格。多个项使用同样的缩进级别作为同一列表的一部分:
- list_value_one
- list_value_two
- list_value_three

列表可以可以作为一个键值对的 value,这个在 tags 里面很多:
db_user:
  - wang
  - zhang
  - li

规则三:冒号

字典的 keys 在 YAML 中的表现形式是一个以冒号结尾的字符串,values 的表现形式是冒号后面的同一行,用一个空格隔开:
my_key: my_value

另一种选择,一个 value 可以通过缩进与 key 联接,注意也是缩紧2个空格,当然通常情况下,一个 key 的 value 不是单一的,而是一个列表的values。

my_key:
  my_value1
  my_value2

其中字典也可以嵌套:
first_key:
  second_key: value_in_second


与其他资料序列语言比较

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

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

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

缩排划界

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

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

"Three is always greater than
two, even for large values of two"

 --Author Unknown   

date: 2007-06-01

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

非阶层式的资料模型

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

实际的考量

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

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

值得讨论的是,尽管它的缩排方式似乎复杂化了深度很大的巢状层次, YAML处理缩小作为一个单一的空间,这可能会取得更好的压缩比的标记语言。此外,极为深刻的缩排可以完全避免的是: (1)使用“内置格式”(即简称JSON样格式)无压痕;或 (2)使用关联锚点放松层次,以形成一个单位的YAML解析器将透明地重新进入全面数据结构。

安全性

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

资料处理和呈现

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

实现与函式库


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

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

Perl相关的函式库
o YAML:: 一个通用的接口,被数个YAML剖析器使用。
o YAML::Tiny YAML简化版的实现。拥有小巧轻快的优点――比完整功能的YAML实现快上许多――并用纯Perl写成。
o YAML::Syck 与SYCK函式库绑定。提供快速,highly featured的YAML剖析器。
o YAML::XS 与LibYaml绑定。提供1.1版更好的相容性。

常见错误与使用细节

* 编辑器:
o 建议使用能将跳格字符自动转换成空白字符的编辑器,并且使用定宽度的字型。
o 编辑器要能正确的处理UTF-8和UTF16编码(或是使用纯ASCII编码――它同时是UTF-8的子集)。

* 字串:
o YAML的字串不需使用引号,这可以增加可读性,并避免巢状的跳脱字符。然而,这有时也会导致错误,例如,字串本身是一个暧昧的字眼(像数字或布林值);或在短句中意外的出现YAML的结构符号(常见的例子是由惊叹号起始的句子,或是包含冒号-空白的句子:"!Caca de vaca!"、"Caution: lions ahead")。这在发布YAML档案时并不造成困扰,但在ad hoc script和人工编辑档案时,这问题还蛮常出现的。比较好的方法是善用区块符号("|" or ">")而不要使用单行字串,来避免这种暧昧的表达示。

最新版本:1.2


官方主页:http://www.yaml.org/


该文章最后由 阿炯 于 2016-04-18 13:05:52 更新,目前是第 2 版。