[TOC]

0x00 前言

YAML是"YAML Ain't a Markup Language"的缩写,它是一种数据序列化语言,并不是一种标记语言
YAML(带有”骆驼”的韵律)是一种基于 Unicode 的基于 Unicode 的数据序列化语言,它围绕敏捷编程语言的常见本机数据类型而设计,它使用 Unicode可打印字符,其中一些字符提供结构信息,其余字符包含数据本身;但是如果你使用过类似XML/JSON这种标记语言(利于人们读写的数据格式),那么你可能会很快的学会YAML,与XML相同的是我们可以使用YAML编写配置文件;

YAML 通过最小化结构字符的数量并允许数据以自然和有意义的方式显示自身, 它对于编程需求(从配置文件到 Internet 消息传递、对象持久性到数据审核)都非常有用。

YAML格式的文件拓展名包括:.yml 和 .yaml,两个都表示YAML文件;

三个基本基元来充分表示:

  • 映射(哈希/字典) mapping
  • 序列(数组/列表) sequence
  • 标量(字符串/数字) scalars

目前经过三个版本:

YAML优缺点:

  • YAML 很容易被人类阅读。
  • YAML 数据可移植到编程语言之间。
  • YAML 与敏捷语言的本机数据结构相匹配。
  • YAML 具有一致的模型来支持通用工具。
  • YAML 支持单通道处理。
  • YAML 具有表现力和可扩展性。
  • YAML 易于实现和使用(独特的清洁)。

应用场景:

  • ansible清单也支持YAML的语法,可以使用YAML语法编写清单从而管理受管主机;
  • docker-compose的DockerFile的文件内容中使用它;
  • 如Perl、Python、PHP、Ruby和Javascript都有对应的解析库

Renfencer


0x01 语法格式

每一个 YAML 文件都是从一个列表开始. 列表中的每一项都是一个键值对, 通常它们被称为一个 “哈希” 或 “字典”. 所以我们需要知道如何在 YAML 中编写列表和字典.
YAML注释:使用#作为注释,并且YAML中只有行注释。

基本格式要求:

  • 1,YAML大小写敏感;
  • 2,使用缩进代表层级关系;
  • 3,缩进只能使用空格不能使用TAB,不要求空格个数,只需要相同层级左对齐(一般2个或4个空格)


常量

YAML中提供了多种常量结构,包括:整数,浮点数,字符串,NULL,日期,布尔,时间,还允许表示三种格式分别是常量值,对象和数组

#即表示url属性值
url: http://www.wolfcode.cn
#即表示server.host属性的值
server:
host: http://www.wolfcode.cn
#数组即表示server为[a,b,c]
server:
- 120.168.117.21
- 120.168.117.22
- 120.168.117.23
#常量
pi: 3.14 #定义一个数值3.14
hasChild: true #定义一个boolean值
boolean:
- TRUE #true,True都可以
- FALSE #false,False都可以
float:
- 3.14
- 6.8523015e+5 #可以使用科学计数法
int:
- 123
- 0b1010_0111_0100_1010_1110 #二进制表示
null:
nodeName: 'node'
parent: ~ #使用~表示null

name: '你好YAML' #定义一个字符串
string:
- 哈哈
- 'Hello world' #可以使用双引号或者单引号包裹特殊字符
- newline
newline2 #字符串可以拆成多行,每一行会被转化成一个空格
date:
- 2018-02-17 #日期必须使用ISO 8601格式,即yyyy-MM-dd
datetime:
- 2018-02-17T15:02:31+08:00 #时间使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区

注意:在定义字符串类型的时里面的字符串你需要使用引号来包裹;


对象

描述:使用冒号代表,格式为key: value,冒号后面要加一个空格;可以使用缩进表示层级关系;

#方式1:常规方式
key:
child-key: value
child-key2: value2

#方式2:YAML中还支持流式(flow)语法表示对象
key: {child-key: value, child-key2: value2}


#方式3:较为复杂的对象格式,可以使用问号加一个空格代表一个复杂的key,配合一个冒号加一个空格代表一个value
?
- Key1
- Key2
:
- Value1
- Value2

意思:即对象的属性是一个数组[key1,key2],对应的值也是一个数组[value1,value2];


数组

描述:使用一个短横线加一个空格代表一个数组项:

#方式1:
array:
- Java
- C++
- C#

#方式2:可以简单理解为:[[Java,LOL]]
-
- Java
- LOL

#方式3:使用流式(flow)的方式
companies: [
{id: 1,name: company1,price: 200W},
{id: 2,name: company2,price: 500W}
]

示例:

#companies属性是一个数组,每一个数组元素又是由id,name,price三个属性构成;
companies:
-
id: 1
name: company1
price: 200W
-
id: 2
name: company2
price: 500W


特殊符号

YAML中提供了很多特殊符号,比如 ---(文档开始) , ...(文档结束) , !!(强制转换) , > (不换行) 与 | (换行) , & (锚点) 与 * (引用) , << (合并) , ~ (代表NULL)

  1. --- YAML可以在同一个文件中,使用—表示一个文档的开始
    #比如Springboot中profile的定义
    server:
    address: 192.168.1.100
    ---
    spring:
    profiles: development
    server:
    address: 127.0.0.1
    ---
    spring:
    profiles: production
    server:
    address: 192.168.1.120


    #代表定义了两个profile,一个是development,一个production;也常常使用---来分割不同的内容,比如记录日志:
    ---
    Time: 2018-02-17T15:02:31+08:00
    User: ed
    Warning:
    This is an error message for the log file
    ---
    Time: 2018-02-17T15:05:21+08:00
    User: ed
    Warning:
    A slightly different error message.


  1. ... 和---配合使用,在一个配置文件中代表一个文件的结束
    #相当于在一个yaml文件中连续写了两个yaml配置项。
    ---
    time: 20:03:20
    player: Sammy Sosa
    action: strike (miss)
    ...
    ---
    time: 20:03:47
    player: Sammy Sosa
    action: grand slam
    ...


  1. !! YAML中使用!!做类型强行转换
    #相当于把数字和布尔类型强转为字符串
    string:
    - !!str 54321
    - !!str true

    #当然允许转型的类型很多,比如:
    --- !!set
    - Mark McGwire: 65
    - Sammy Sosa: 63
    - Sammy Sosa: 63
    - Ken Griffy: 58

将数组解析为set,简单理解转化的内容就是:[{Ken Griffy=58}, {Mark McGwire=65}, {Sammy Sosa=63}],重复的Sammy Sosa去掉;


  1. >在字符串中折叠换行(变成单行), |保留换行符这两个符号是YAML中字符串经常使用的符号,比如
#示例1. > 与 | 符号的不同
accomplishment: >
Mark set a major league
home run record in 1998.
stats: |
65 Home Runs
0.278 Batting Average

#执行结果
# accomplishment=Mark set a major league home run record in 1998.

# stats=65 Home Runs
# 0.278 Batting Average,

# |符号常见用于在YAML中配置HTML片段:
phraseTemplate: |
<p style="color: red">
some template ${msg}
</p>

即将换行符转化成了空格;要注意一点的是,每行的文本前一定要有一个空格


  1. 引用重复的内容在YAML中可以使用&来完成锚点定义,使用*来完成锚点引用
    #在hr中,使用&SS为Sammy Sosa设置了一个锚点(引用)名称为SS,在rbi中使用*SS完成了锚点使用
    hr:
    - Mark McGwire
    - &SS Sammy Sosa
    rbi:
    - *SS
    - Ken Griffey

    #执行结果
    {rbi=[Sammy Sosa, Ken Griffey], hr=[Mark McGwire, Sammy Sosa]}


    #方式2:注意不能独立的定义锚点,比如不能直接这样写: &SS Sammy Sosa;
    SS: &SS Sammy Sosa
    hr:
    - Mark McGwire
    - *SS
    rbi:
    - *SS
    - Ken Griffey


    #方式3.另外锚点能够定义更复杂的内容,比如:那么hr相当于引用了default的数组,
    default: &default
    - Mark McGwire
    - Sammy Sosa
    hr: *default #注意hr: *default要写在同一行。


6.合并内容 << 。主要和锚点配合使用,可以将一个锚点内容直接合并到一个对象中。
来看一个示例:

#在merge中,定义了四个锚点,分别在sample中使用。
merge:
- &CENTER { x: 1, y: 2 }
- &LEFT { x: 0, y: 2 }
- &BIG { r: 10 }
- &SMALL { r: 1 }

#sample1中,<<: *CENTER意思是引用{x: 1,y: 2},并且合并到sample1中,那么合并的结果为:sample1={r=10, y=2, x=1}
sample1:
<<: *CENTER
r: 10

#sample2中,<<: [*CENTER, *BIG] 意思是联合引用{x: 1,y: 2}和{r: 10},并且合并到sample2中,那么合并的结果为:sample2={other=haha, x=1, y=2, r=10}
sample2:
<< : [ *CENTER, *BIG ]
other: haha

#sample3中,引入了*CENTER, *BIG,还使用了r: 100覆盖了引入的r: 10,所以sample3值为:sample3={r=100, y=2, x=1}
sample3:
<< : [ *CENTER, *BIG ]
r: 100

有了合并,我们就可以在配置中,把相同的基础配置抽取出来,在不同的子配置中合并引用即可。


0x02 补充示例

编码存放
#yaml配置文件,demo是一个数组,成员是一个属性值
---
demo:
- unicode: "Sosa did fine.\u263A"
- control: "\b1998\t1999\t2000\n"
- hex esc: "\x0d\x0a is \r\n"
- single: '"Howdy!" he cried.'
- quoted: ' # Not a ''comment''.'
- tie-fighter: '|\-*-/|'

#转换结果
{ demo:
[ { unicode: 'Sosa did fine.☺' },
{ control: '\b1998\t1999\t2000\n' },
{ 'hex esc': '\r\n is \r\n' },
{ single: '"Howdy!" he cried.' },
{ quoted: ' # Not a \'comment\'.' },
{ 'tie-fighter': '|\\-*-/|' } ] }


时间时区
#yaml配置文件
timestamp:
canonical: 2001-12-15T02:59:43.1Z
valid iso8601: 2001-12-14t21:59:43.10-05:00
space separated: 2001-12-14 21:59:43.10 -5
no time zone (Z): 2001-12-15 2:59:43.10
date (00:00:00Z): 2002-12-14

#执行结果
timestamp:
{ canonical: Sat Dec 15 2001 10:59:43 GMT+0800 (中国标准时间),
'valid iso8601': Sat Dec 15 2001 10:59:43 GMT+0800 (中国标准时间),
'space separated': Sat Dec 15 2001 10:59:43 GMT+0800 (中国标准时间),
'no time zone (Z)': Sat Dec 15 2001 10:59:43 GMT+0800 (中国标准时间),
'date (00:00:00Z)': Sat Dec 14 2002 08:00:00 GMT+0800 (中国标准时间) },


yaml正则
#全局对象-正则匹配
regexp:
simple: !!js/regexp foobar
modifiers: !!js/regexp /foobar/mi
# regexp: { simple: /foobar/, modifiers: /foobar/im },

undefined: !!js/undefined ~
# undefined: undefined,

function: !!js/function >
function foobar() {
return 'Wow! JS-YAML Rocks!';
}
# function: [Function: anonymous],