配置语言

Yaml 是一种用于写配置文件的语言。我个人觉得写配置文件并不需要语言具有过强的计算能力,而是需要语言可以更加清晰的描述配置的数据结构。

Yaml 的基本语法规则如下:

  • 大小写敏感
  • 使用 缩进 来描述层次结构,缩进不能使用 tab 只能用 space ,缩进的多少不重要,重点是可以对齐
  • #注释
  • 支持 字典,数组,纯量 3 种数据结构

Yaml 语法简单(最难的居然是用空格左对齐),介绍 Yaml 较好的教程是阮一峰博客 ,此外对于拿不准的语法,可以在这个网站转换成 JSON 格式查看。

语法

字典

字典就是一组 Key-Value 对,用 : 声明一个 Key-Value 对,如下所示:

name: Steve
age: 18

如果用 JSON 来表达就是:

{ "name": 'Steve', "age": 18 }

字典还有一种行内表达方式,如下所示:

{ name: Steve, age: 18 } 

数组

一组连词线开头的行,构成一个数组。

- Cat
- Dog
- Goldfish

对应的 JSON 是:

[ 'Cat', 'Dog', 'Goldfish' ]

Yaml 同样提供了数组的行内表示法:

[Cat, Dog, Goldfish]

数据结构的子成员是一个数组,则可以在该项下面缩进一个空格:

-
 - Cat
 - Dog
 - Goldfish

对应的 JSON 是:

[ [ 'Cat', 'Dog', 'Goldfish' ] ]

纯量

其他

Yaml 提供了多种数据类型,不过其实可以分为字符串类和非字符串类,字符串类的语法要更加复杂,而其他数据类型的语法非常简单直白。

  • 布尔值: true, false, True, False, TRUE, FALSE
  • null: ~ ,如果空着也默认是 null
  • 整数: 12, 0b1010, 0x77, 0xff
  • 浮点数: 3.14, 6.8523015e+5
  • 日期: 2018-02-17
  • 时间: 2018-02-17T15:02:31+08:00

其中布尔值可以具有多种写法,整数可以使用多种数制,浮点数可以使用科学计数法,日期和时间遵循的都是 ISO-8601 格式,对于日期,需要用 yyyy-MM-dd 格式,对于时间,时间和日期之间使用 T 连接,最后使用 + 代表时区。

字符串

  1. 冲突

    Yaml 中的字符串默认是不用引号的,如下所示:

    str: 这是一行字符串

    对应的 JSON 就是

    { "str": '这是一行字符串' }

    这就引发了一个问题,就是其他类型也不用引号,所以我们没法简单的表达一个 'true' 类型,如果我们这么写

    str: true

    是不起作用的。所以要么可以使用强制类型转换:

    str: !!str true

    需要用引号显式声明:

    str: 'true'

    用引号还可以避免如 :, - 这样的关键词干扰。

    双引号和单引号都可以,单引号不会对特殊字符转义。单引号之中如果还有单引号,必须连续使用两个单引号转义。

  2. 多行字符串

    字符串可以写成多行,从第二行开始,必须有一个单空格缩进。换行符会被转为空格。

    str: 这是一段
      多行
      字符串

    对应的 JSON 为:

    { "str": '这是一段 多行 字符串' }

    多行字符串可以使用 | 保留换行符,也可以使用 > 折叠换行:

    this: |
      Foo
      Bar
    that: >
      Foo
      Bar

    对应的 JSON 为:

    { "this": 'Foo\nBar\n', "that": 'Foo Bar\n' }

    + 表示保留文字块末尾的换行, - 表示删除字符串末尾的换行。

    s1: |
      Foo
     
    s2: |+
      Foo
     
    s3: |-
      Foo

    对应的 JSON 为:

    { "s1": 'Foo\n', "s2": 'Foo\n\n\n', "s3": 'Foo' }

引用

我其实没有太看懂,其中 & 用于声明一个引用, * 用来使用这个引用。关键是 & 似乎只能在两个特殊的位置出现,一个是 key:: 之后,用 * 会获得 key 所表示的节点,如下所示:

this: &label 
  - son0
  - son1 
that: *label

对应的 JSON 是:

{ "this": [ 'son0', 'son1' ], "that": [ 'son0', 'son1' ] }

另一种是表示数组的 - 之后,纯量之前(不是纯量就不行),如下所示:

- &label scalar
- *label

对应的 JSON 是:

[ 'scalar', 'scalar' ]

<< 表示将引用的内容合并到当前字典,局限性似乎非常大:

defaults: &defaults
  adapter:  postgres
  host:     localhost
 
development:
  database: myapp_development
  <<: *defaults

等价于

defaults:
  adapter:  postgres
  host:     localhost
 
development:
  database: myapp_development
  adapter:  postgres
  host:     localhost

缩进层次结构

我个人感觉比较难以理解的是无论是字典还是数组,都没有像其他语言里那种明确的 [], {} 分界符,以致于不熟悉时,很难分清某段代码,是“有 3 个元素的数组”,还是“只有 1 个元素的数组,这个元素是一个有 3 个元素的数组”?

其实关键在于“缩进”,有如下规则:

  • 利用缩进可以形成一颗树
  • 兄弟节点必须是相同类型,要么用 : ,要么用 -