YAML,另一种标记语言?不止是标记语言!

YAML是”Yet Another Markup Language”(另一种标记语言)的缩写。但后来项目的维护者将其重命名成了”YAML Ain‘t Markup Language”(YAML 不止是标记语言),以更加强调其面向数据的特性。所以你看,文章的标题,并不是我想标题党,是我真不知道起啥标题好才拿过来用的~。

一个简单的YAML文件

让我们先来看一个YAML文件的示例,做一个简要的概述。

--- 
 doe: "a deer, a female deer"
 ray: "a drop of golden sun"
 pi: 3.14159
 xmas: true
 french-hens: 3
 calling-birds: 
   - huey
   - dewey
   - louie
   - fred
 xmas-fifth-day: 
   calling-birds: four
   french-hens: 3
   golden-rings: 5
   partridges: 
     count: 1
     location: "a pear tree"
   turtle-doves: two

文件以三个短横线开头。这些短横线表示新YAML文档的开始。YAML支持在单个文件里存放多个文档,解析器会将每组短横线识别为新文档的开始。

接下来,我们将看到构成大部分典型YAML文档的常用结构:键-值对。Doe是一个指向字符串值"a deer, a female deer."的键。

YAML支持的不仅仅是字符串值。上面的文件以六个键-值对开始。它们的值有四种不同的数据类型。键doeray的值都是字符串。pi是一个浮点数。xmas是一个布尔值。french-hens是一个整数。可以用单引号或双引号将字符串括起来,也可以根本不用引号。YAML将无引号的数字识别为整数或浮点数。

第七项是个数组。calling-birds有四个元素,每个数组元素都由一个短横线开头。

我用两个空格缩进了calling-birds中的元素。缩进是YAML表示嵌套的方式。空格缩进的数量可以因文件而异,相同层级的元素左侧对齐即可,但是不允许使用Tab制表符。

最后,我们看到xmas-fifth-day,里面还有五个元素,每个元素都缩进了。我们可以将xmas-fifth-day视为包含两个字符串、两个整数和另一个字典值的字典。YAML支持这种键值嵌套和混合类型。

在我们深入研究YAML之前,让我们先看看这个文档用JSON表示是什么样子。可以在百度或者Google上搜一下YAMLJSON的网页工具,这类工具有很多。

{
  "doe": "a deer, a female deer",
  "ray": "a drop of golden sun",
  "pi": 3.14159,
  "xmas": true,
  "french-hens": 3,
  "calling-birds": [
    "huey",
    "dewey",
    "louie",
    "fred"
  ],
  "xmas-fifth-day": {
    "calling-birds": "four",
    "french-hens": 3,
    "golden-rings": 5,
    "partridges": {
      "count": 1,
      "location": "a pear tree"
    },
    "turtle-doves": "two"
  }
}

缩进和空白符

空白符是YAML格式的一部分,其中换行符表示字段的结束,缩进用于组织YAML文档的结构,用来表示文档的层级或者叫嵌套关系。不过YAML值允许使用空格符进行缩进,不允许使用制表符Tab键,这是因为不同的工具对待制表符的方式不同。缩进的空格数目不重要,只要相同层级的元素左侧对齐即可,比如下面这个文档,因为同级元素未对齐,在解析器里会报错

foo: bar
     pleh: help
     stuff:
       foo: bar
       bar: foo

所以总结下来记住下面四点即可:

  • 换行符表示字段的结束。
  • 使用缩进表示层级关系。
  • 缩进时不允许使用制表符Tab键,只允许使用空格。
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可。

注释

YAML中的注释以#号开头。它们可以出现在字段值之后,也可以占据整行。

___
# This is a full line comment
foo: bar # this is a comment, too

YAML数据类型

键-值对是YAML的基本构建块。YAML文档中的每一项都是至少一个字典的成员。键的类型始终是字符串。

YAML的键-值对中的值是标量。它们的表现行为类似于JavascriptPython等语言中的标量类型。通常我们只要记住将字符串括在引号中,数字不加引号就足够了,解析器会自动解析出值的类型。

下面我们就详细看一下YAML的值都支持哪些数据类型,我们先从数组和字典这两种符合类型说起,然后再逐步拆解看里面的普通类型。一般看完这两种复合类型就可以掌握YAML的基本用法,上手编写和修改YAML文档了。

数组

YAML的数组可以写在一行里

items: [ 1, 2, 3, 4, 5 ]
names: [ "one", "two", "three", "four" ]

也可以写在多行里,每一行指定一个元素

---
items:
  - 1 
  - 2 
  - 3
  - 4 
  - 5 
names:
  - "one"
  - "two"
  - "three"
  - "four"

多行格式对于包含复杂对象的列表非常有用。

___
items:
  - things:
      thing1: huey
      things2: dewey
      thing3: louie
  - other things:
      key: value

数组可以包含任何有效的YAML值。同一个数组中的值不必是同一类型。

字典

与数组一样,可以将字典放入单行内。我们在上面看到了这种格式。类型Python打印字典的格式:

---
foo: { thing1: huey, thing2: louie, thing3: dewey }

也见过在多行里指定字典元素的格式:

---
foo: bar
bar: foo

当然字典也可以自由嵌套,并且持有任何有效的YAML值。

---
foo:
  bar:
    - bar
    - rab
    - plop

数字类型

YAML可以识别数字类型。我们在上面看到了浮点和整数。YAML还支持其他几种数字类型。

整数可以用十进制、十六进制或八进制表示,Ox表示一个值是十六进制的,前导零表示一个八进制值。

 foo: 12345
 bar: 0x12d4
 plop: 023332

转换成JSON后整数都是十进制表示的

{
  "foo": 12345,
  "bar": 4820,
  "plop": 9946
}

YAML既支持固定浮点数,也支持指数浮点数。

 foo: 1230.15
 bar:  12.3015e+05

最后YAML里也可以表示正负无穷和非数字NaN (Not a Number的缩写)

foo: .inf
bar: -.Inf
plop: .NAN

字符类型

YAML的字符串为Unicode编码。在大多数情况下,不必用引号将它们括起来。

foo: this is a normal string

但是如果我们想要处理转义序列,我们需要使用双引号的字符串。

foo: "this is not a normal string\n"
bar: this is not a normal string\n

YAML将上面第一个值处理为以回车符和换行符结尾。由于第二个值未加引号,因此YAML\n视为两个普通的字符。

foo: this is not a normal string
bar : this is not a normal string\n

YAML不会对使用单引号的字符串进行转义,但是单引号可以避免将字符串内容解释为文档格式。

字符串值可以写在多行里。使用大于号,可以指定字符串块。

bar: >
  this is not a normal string it
  spans more than
  one line
  see?

但是解析器会把他们解析成单行字符串:

bar : this is not a normal string it spans more than one line see?

如果想让YAML把上面的字符串也解析成多行的,需要使用管道符  |

bar: |
  this is not a normal string it
  spans more than
  one line
  see?

Null

YAML里用波浪号~或者不带引号的null直接表示空值。

foo: ~
bar: null

布尔值

YAML用关键字TrueOnYes表示真。用FalseOffNo表示假。

---
foo: True
bar: False
light: On
TV: Off

高级选项

多文档

YAML文档以三个短横线开始,以三个英文句号结束。一些YAML处理程序会强制要求文档以三个短横线开始,结束运算符通常是可选的。例如,JavaJackson不会在没有开始的情况下处理YAML文档,但是PythonPyYAML会。

当一个文件包含多个文档时,通常使用文档结束操作符。比如下面这个例子

---
bar: foo
foo: bar
...
---
one: two
three: four

使用下面的Python脚本解析这个YAML文件:

import yaml

if __name__ == '__main__':
    stream = open("foo.yaml", 'r')
    dictionary = yaml.load_all(stream)

    for doc in dictionary:
        print("New document:")
        for key, value in doc.items():
            print(key + " : " + str(value))
            if type(value) is list:
                print(str(len(value)))

脚本会解析出YMAL文件里的两个文档对象:

New document:
bar : foo
foo : bar
New document:
one : two
three : four

总结

YAML是一种功能强大的语言,可用于配置文件、应用程序之间的消息和保存应用程序状态。我们介绍了它最常用的特性,包括如何使用内置数据类型和构造复杂文档。它还支持自定义函数、正则表达式这样的高级功能。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注