一、简介

YAML 是专门用来写配置文件的语言,非常简洁和强大,远比 JSON 格式方便。

基本语法规则如下:

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进时不允许使用Tab键,只允许使用空格。
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
  • # 表示注释

YAML 支持的数据结构有三种。

  • 对象:
    键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
  • 数组:
    一组按次序排列的值,又称为序列(sequence) / 列表(list)
  • 纯量(scalars):
    单个的、不可再分的

二、对象

对象的一组键值对,使用冒号结构表示。

1
2
animal: pets
hash: { name: Steve, foo: bar }

转为JavaScript :

1
2
{ animal: 'pets' }
{ hash: { name: 'Steve', foo: 'bar' } }

三、数组

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

1
2
3
- Cat
- Dog
- Goldfish

转为 JavaScript

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

嵌套数组,则在该项下面缩进一个空格。

1
2
3
4
-
- Cat
- Dog
- Goldfish

转为JS :

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

数组也可以采用行内表示法。

1
animal: [Cat, Dog]

转为 JavaScript

1
{ animal: [ 'Cat', 'Dog' ] }

四、复合结构

对象和数组可以结合使用,形成复合结构。

1
2
3
4
5
6
7
8
9
languages:
- Ruby
- Perl
- Python
websites:
YAML: yaml.org
Ruby: ruby-lang.org
Python: python.org
Perl: use.perl.org

转为 JavaScript 如下。

1
2
3
4
5
6
{ languages: [ 'Ruby', 'Perl', 'Python' ],
websites:
{ YAML: 'yaml.org',
Ruby: 'ruby-lang.org',
Python: 'python.org',
Perl: 'use.perl.org' } }

五、纯量

纯量是最基本的、不可再分的值。以下数据类型都属于 JavaScript 的纯量。

  1. 字符串
  2. 布尔值
  3. 整数
  4. 浮点数
  5. Null
  6. 时间
  7. 日期

表示如下:

  1. 数值直接以字面量的形式表示。
  2. 布尔值用truefalse表示。
  3. null~表示。
  4. 时间采用 ISO8601 格式。
  5. 日期采用复合 iso8601 格式的年、月、日表示。
  6. YAML 允许使用两个感叹号,强制转换数据类型。
1
2
3
4
5
6
7
8
9
10
11
12
# 数值直接以字面量的形式表示。
number: 12.30
# 布尔值用`true`和`false`表示。
isSet: true
# null用~表示。
parent: ~
# 时间采用 ISO8601 格式。
iso8601: 2001-12-14t21:59:43.10-05:00
# 日期采用复合 iso8601 格式的年、月、日表示。
date: 1976-07-31
# YAML 允许使用两个感叹号,强制转换数据类型。
e: !!str 123

对应的js

1
2
3
4
5
6
{ number: 12.30 }
{ isSet: true }
{ parent: null }
{ iso8601: new Date('2001-12-14t21:59:43.10-05:00') }
{ date: new Date('1976-07-31') }
{ e: '123'}

六、字符串

  1. 字符串默认不使用引号表示
  2. 如果字符串之中包含空格或特殊字符,需要放在引号之中
  3. 单引号和双引号都可以使用,双引号不会对特殊字符转义。
  4. 单引号之中如果还有单引号,必须连续使用两个单引号转义。
  5. 字符串可以写成多行,从第二行开始,必须有一个单空格缩进。换行符会被转为空格。
  6. 多行字符串可以使用|保留换行符,也可以使用>折叠换行。
  7. +表示保留文字块末尾的换行,-表示删除字符串末尾的换行。
  8. 字符串之中可以插入 HTML 标记。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 字符串默认不使用引号表示
str: 这是一行字符串
# 如果字符串之中包含空格或特殊字符,需要放在引号之中
str: '内容: 字符串'
# 单引号和双引号都可以使用,双引号不会对特殊字符转义。
s1: '内容\n字符串'
s2: "内容\n字符串"
# 单引号之中如果还有单引号,必须连续使用两个单引号转义。
str: 'labor''s day'
# 字符串可以写成多行,从第二行开始,必须有一个单空格缩进。换行符会被转为空格。
str: 这是一段
多行
字符串
# 多行字符串可以使用|保留换行符,也可以使用>折叠换行。
this: |
Foo
Bar
that: >
Foo
Bar
# +表示保留文字块末尾的换行,-表示删除字符串末尾的换行。
s1: |
Foo

s2: |+
Foo


s3: |-
Foo
# 字符串之中可以插入 HTML 标记。
message: |

<p style="color: red">
段落
</p>

转为 JavaScript 如下。

1
2
3
4
5
6
7
8
{ str: '这是一行字符串' }
{ str: '内容: 字符串' }
{ s1: '内容\\n字符串', s2: '内容\n字符串' }
{ str: 'labor\'s day' }
{ str: '这是一段 多行 字符串' }
{ this: 'Foo\nBar\n', that: 'Foo Bar\n' }
{ s1: 'Foo\n', s2: 'Foo\n\n\n', s3: 'Foo' }
{ message: '\n<p style="color: red">\n 段落\n</p>\n' }

七、引用

锚点&和别名*,可以用来引用

1
2
3
4
5
6
7
8
9
10
11
defaults: &defaults
adapter: postgres
host: localhost

development:
database: myapp_development
<<: *defaults

test:
database: myapp_test
<<: *defaults

等同于下面的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
defaults:
adapter: postgres
host: localhost

development:
database: myapp_development
adapter: postgres
host: localhost

test:
database: myapp_test
adapter: postgres
host: localhost

&用来建立锚点(defaults),<<表示合并到当前数据,*用来引用锚点。

1
2
3
4
5
- &showell Steve 
- Clark
- Brian
- Oren
- *showell

转为 JavaScript 代码如下。

1
[ 'Steve', 'Clark', 'Brian', 'Oren', 'Steve' ]

八、分段

在同一个yaml文件中,可以用 — 来分段,这样可以将多个文档写在一个文件中

1
2
3
4
5
6
---
name: James
age: 20
---
name: Lily
age: 19

和python交互

PyYaml

load() :返回一个对象

1
2
3
4
5
6
7
8
9
10
11
# config.yaml
name: Tom Smith
age: 37
spouse:
name: Jane Smith
age: 25
children:
- name: Jimmy Smith
age: 15
- name1: Jenny Smith
age1: 12
1
2
3
4
5
6
# main.py
import yaml
f = open('config.yaml')
y = yaml.load(f)
print(y)
# {'name': 'Tom Smith', 'age': 37, 'spouse': {'name': 'Jane Smith', 'age': 25}, 'children': [{'name': 'Jimmy Smith', 'age': 15}, {'name1': 'Jenny Smith', 'age1': 12}]}

load_all()生成一个迭代器

如果string或文件包含几块yaml文档,你可以使用yaml.load_all来解析全部的文档。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import yaml

f = '''
---
name: James
age: 20
---
name: Lily
age: 19
'''

y = yaml.load_all(f)
for data in y:
print(data)
# {'name': 'James', 'age': 20}
# {'name': 'Lily', 'age': 19}

yaml.dump 将一个python对象生成为yaml文档

1
2
3
4
5
6
7
8
9
10
11
import yaml

aproject = {'name': 'Silenthand Olleander',
'race': 'Human',
'traits': ['ONE_HAND', 'ONE_EYE']
}

print(yaml.dump(aproject,))
# name: Silenthand Olleander
# race: Human
# traits: [ONE_HAND, ONE_EYE]

yaml.dump接收的第二个参数一定要是一个打开的文本文件或二进制文件,yaml.dump会把生成的yaml文档写到文件里

1
2
3
4
5
6
7
8
import yaml

aproject = {'name': 'Silenthand Olleander',
'race': 'Human',
'traits': ['ONE_HAND', 'ONE_EYE']
}
f = open('config.yaml','w')
print(yaml.dump(aproject,f))

yaml.dump_all()将多个段输出到一个文件中

1
2
3
4
5
6
7
import yaml

obj1 = {"name": "James", "age": 20}
obj2 = ["Lily", 19]

with open('config.yaml', 'w') as f:
yaml.dump_all([obj1, obj2], f)

构造器、表示器、解析器

构造器(constructors)、表示器(representers)、解析器(resolvers )

1、yaml.YAMLObject
yaml.YAMLObject用元类来注册一个构造器(也就是代码里的 init() 方法),让你把yaml节点转为Python对象实例,用表示器(也就是代码里的 repr() 函数)来让你把Python对象转为yaml节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import yaml

class Person(yaml.YAMLObject):
yaml_tag = '!person'

def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return '%s(name=%s, age=%d)' % (self.__class__.__name__, self.name, self.age)


james = Person('James', 20)
print(yaml.dump(james)) # Python对象实例转为yaml

lily = yaml.load('!person {name: Lily, age: 19}')
print(lily) # yaml转为Python对象实例

# 输出:
# !person {age: 20, name: James}
#
# Person(name=Lily, age=19)

2、yaml.add_constructor 和 yaml.add_representer

你可能在使用过程中并不想通过上面这种元类的方式,而是想定义正常的类,那么,可以用这两种方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import yaml


class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age

def __repr__(self):
return 'Person(%s, %s)' % (self.name, self.age)

james = Person('James', 20)
print (yaml.dump(james)) # 没加表示器之前


def person_repr(dumper, data):
return dumper.represent_mapping(u'!person', {"name": data.name, "age": data.age}) # mapping表示器,用于dict

yaml.add_representer(Person, person_repr) # 用add_representer方法为对象添加表示器
print (yaml.dump(james)) # 加了表示器之后


def person_cons(loader, node):
value = loader.construct_mapping(node) # mapping构造器,用于dict
name = value['name']
age = value['age']
return Person(name, age)

yaml.add_constructor(u'!person', person_cons) # 用add_constructor方法为指定yaml标签添加构造器
lily = yaml.load('!person {name: Lily, age: 19}')
print (lily)

# 输出:
# !!python/object:__main__.Person {age: 20, name: James}
#
# !person {age: 20, name: James}
#
# Person(Lily, 19)