YAML是『YAML Ain't Markup Language』的缩写,又一个递回式缩写的例子。发音是『Camel』,也就是英文骆驼的发音。通常,我们看到xxML的标准,都会想到这是不是又一个所谓的Markup Language。刚好相反,正如其名称所显示的,YAML是与Markup Language相反,他是要使用不同的方式来表示Markup Language所能够做到的功能。简单的说,YAML利用适当文字的缩排,及一些基本符号来达到Markup Language的所能够表示阶层以及自订结构的功能。

 

所谓的Markup Language,的确可以让机器很容易的进行处理。但是要让人类能够轻易的理解,似乎并非这么的容易。YAML就是除了要让机器可以容易处理,也让人类可以很容易的了解以及编辑。 总体来说,YAML的设计目的如下:

  • YAML的文件能够很容易被人类所读取及了解。
  • YAML可用来表示程式语言的原生资料结构。
  • YAML的资料可以在不同的程式语言间移植。
  • YAML有一个一致的模型来支援一般的工具。
  • YAML能够使用串流的方式(stream-based)来处理。
  • YAML具有丰富的表达能力及可扩充性。
  • YAML可以容易被实作及使用。

YAML如果没有特别的指定,是使用Unicode作为其预设使用字元集。 一份YAML文件可以由好几个无关的文件所组成。依据YAML的规定,每份文件的启始需用"---"表示之。而结尾需用"..."表示之。有时结尾可以省略,当遇到另一个"---"出现,或是档案结尾,就表示目前这份文件结束了。例如:


---
This is a test. This
document is used to introduce YAML.
---
You can understand how to use YAML.
...


对于程式语言来说,所有的资料结构,原则上都可以用下面三种方式来表示:

  • Mappings (hashes/dictionaries)
  • Sequences (arrays/lists)
  • Scalars (strings/numbers)

所谓的Mapping主要有两个部分所组成一个是Key,另一个Value。一组Key与Value称为一个Key/Value pair。这样的一组pair代表一组资料,一个Mapping有许许多多的Key/Value pair所组成。而Sequence所代表的是一个一个资料所形成的串列。Scalars则是我们常见的数字及字串等资料。

先让我们看一下Mapping的用法。因为,Mapping由两个部分所组成,Key及Value。在YAML中一个Key/Value Pair是使用冒号":"将这两者予以分隔。假设我们将个人资料用YAML来表示。可以表示成下面的方式:

Name: Gary Lee Birth: 1970-01-01 Score: 0.92

是不是很容易理解呢?其实这种表示法在MIME这种E-Mail标准中使用。所以,对于机器来说这样的表示法处理起来并不会有特别的困难。另外,对于YAML来说,多余的空白字元会被忽略的,所以对于上面的例子来说,用下面几种写法也是一样的。

Name: Gary Lee Birth: 1970-01-01 Score: 0.92

Name : Gary Lee Birth : 970-01-01 Score : 0.92

虽说空白字元是多余的。不过在冒号后面至少要有一个空白字元。这是YAML所强制规定的。 接着我们来看一下Sequence的表示方式。依据YAML的规定,Sequence的每个项目前面都应该使用"-"来作为识别符号。与Mapping相同的是,在"-"后面也需要有至少一个空白字元。

举例来说,我们有几个人名:Gary, David, Tom, Jim等。用Sequences来表示就如下所示:

- Gary - David - Tom - Jim

当然,程式内的资料结构往往没有这么简单,举例来说,像是Sequences中的Sequences,或是Mapping中的Mapping这样的资料,要如何表示呢?YAML另外制订了两个方式来表示简单的Sequence及Mapping。

简易的Sequence表示法:

[item1, item2, ..., itemN]

所以,一个Sequence中的Sequence可以写成下面这样:

- [Name, Birth, Score] - [Gary, 1977-02-12, 1.0] - [David, 1972-05-21, 0.93] - [Tom, 1979-12-12, 0.82]

另外,简易的Mapping表示法:

{Key1: Value1, Key2: Value2, ..., KeyN: ValueN}

所以,Mapping中的Mapping可以用下面的写法:

Gary:  {Birth: 1977-02-12, Score: 1.0}
David: {Birth: 1972-05-21, Score: 0.93}
Tom:   {
        Birth: 1979-12-12,
        Score: 0.82
        }

有些时候,Mapping所使用的Key可能不是单纯的型态。Mapping的Key也可以是Sequence 或是其他更复杂的资料型态。只是这种Key必需在前面加上"?"的符号。例如:

? [Gary,
   Tom,
   Jim]
: { Birth: 2004-04-20,
    City: Taipei }

在YAML中,也支援注解,所谓的注解就是用来说明资料本身的项目。并不代表资料本身。要使用注解就是使用"#"。例如:

# Gary's profile.
Gary:  {
    Birth: 1977-02-12,
    Score: 1.0 }

在YAML用一种简化的方式可以让使用者重复使用定义过的Node。首先,您需要给打算重复使用的Node一个名字。这个名字前面需要用"&"作为起始。然后要重复使用时,就使用"*"加上该名字。例如:

Name:    &GL Gary Lee
Birth:   1970-01-01
Score:   0.92
Alias:   *GL

当然,以上的功能是不足以应付更复杂的资料表示方式,甚至是像XML具有扩充的能力。因此,YAML提供了更复杂的资料表示方式。资料结构一般都会使用阶层式的方式来表示资料。对于XML来说,就是使用巢状式标签来表示。但是YAML用了另外一种人类更容易的方式来表示。也就是『缩排』(indentation)。

YAML建议缩排不要使用Tab,而是使用一般的空白字元。这是因为Tab容易因为编辑器的不同,而产生不同排版效果。这样容易被误解。

我们看一下的范例:

Name: Gary
Profile:
    Birth: 1975-04-43
    Addr:
        - City: Taipei
          Road: Chung-Shoang
          Number: 20
          Flood: 3
        - City: TaoYuan
          Road: LongChung
          Number: 10
          Flood: 2
    Blood Type: A
Score: 0.98

其实,不用多说,我相信读者也看的出来这些资料的阶层关系。这就是YAML所强调的,可以很容易的被人类所读懂。这边比较需要说明的是Address。我们前面说过"-"是用来表示Sequences。所以,Addr的Value一共有两个项目,也就是说有两个地址。

您可以依照自己的需要制订任意的阶层。当然也可以在各阶层之下混用各种YAML所支援的资料型态。

有些时候,资料太多时,一行可能放不下。这时候可能要分成多行。在YAML中提供了两种方式可以让你把一个资料分成多行。第一个方式是使用"|"符号在资料的最前面。例如:

Self_Intro: |
    Hi. My name is gary. This article
    is used to help you understand how
    to use YAML.


另外,还有一种方式是使用">"符号。

Self_Intro: >
    Hi. My name is gary. This article
    is used to help you understand how
    to use YAML.



这两种方式有什么不同呢?唯一的差别在于"|"符号会保留『换行』。但是,在">"中,换行字元会被视为空白字元。

一般的情况,没有特别指定的话,YAML会依据下面的方式来辨别资料型态。

整数:
    标准(canonical): 12345
    十进位(decimal): +12,345
    六十进位法(sexagesimal): 3:25:45
    八进位(octal): 014
    十六进位(hexadecimal): 0xC

浮点数:
    标准(canonical): 1.23
    科学记号(exponential): 12.3015e+02
    六十进位法(sexagesimal): 20:30.15
    固定表示法(fixed): 1,230.15
    负的无限数(negative infinity): (-inf)
    非数字(not a number): (NaN)

其他:
    null: ~
    真(true): y
    伪(false): n
    字串(string): '12345'

时间:
    标准(canonical): 2001-12-15T02:59:43.1Z
    ISO 8601所定义的标准表示法(iso8601):  2001-12-14t21:59:43.10-05:00
    间隔表示法(spaced):  2001-12-14 21:59:43.10 -05:00
    日期(date):   2002-12-14



有些时候,你也可以直接指定要使用型态。此时,您需要在只指定的资料前面加上"!"符号,并且写上型态的名称。例如:

birth: !date 2004-04-29
picture: !binary |
    R0lGODlhDAAMAIQAAP//9/X
    17unp5WZmZgAAAOfn515eXv
    Pz7Y6OjuDg4J+fn5OTk6enp
    56enmleECcgggoBADs=



另外,有些时候你或许要使用预先定义的好的资料型态,而这个资料型态可能是别的URL所指定的档案中所定义的。此时,您需要在文件的开头指定要参考哪里的档案。并且在使用指定的型态时,前面加上"^"符号。例如:

--- !clarkevans.com,2002/graph/^shape
- !^circle
  center: &ORIGIN {x: 73, y: 129}
  radius: 7
- !^line
  start: *ORIGIN
  finish: { x: 89, y: 102 }



最后,我们用一个较大的范例作个结尾:

---
Name: Tom Lee
Profile:
    Birth: 1975-04-43
    Addr:
        - City: Taipei
          Road: Chung-Shoang
          Number: 20
          Flood: 3
        - City: TaoYuan
          Road: LongChung
          Number: 10
          Flood: 2
    Blood Type: A
Relative:
    - Type: Mother
      Name: Peggy
      Birth: 1943-12-1
      Notes:
        - Living in U.S.
        - Have five children.
    - Type: Brother
      Name: Jim
      Birth: 1942-12-3
      Notes: ~
Photo:
    - !binary |
      R0lGODlhDAAMAIQAAP//9/X
      17unp5WZmZgAAAOfn515eXv
      Pz7Y6OjuDg4J+fn5OTk6enp
      56enmleECcgggoBADs=
Score: 0.98
...