YAML快速入门

前言

最近在学习微服务和Spring Cloud开发相关知识,在Spring Boot中,推荐使用properties或者YAML文件来完成配置,所以一开始学习使用的配置文件是properties文件,但是properties文件在表示层级关系和结构关系的方面有所欠缺,对于复杂的数据结构,YAML文件表示的清晰度会远远大于properties文件,所以我们使用YAML文件完成Spring Cloud的配置。

properties文件:

1
2
3
4
server.port: 8761
eureka.client.registerWithEureka: false
eureka.client.fetchRegistry: false
eureka.client.serviceUrl.defaultZone: http://localhost:8761/eureka/

YAML文件:

1
2
3
4
5
6
7
8
server:
port: 8761
eureka:
client:
registerWithEureka: false #是否将自己注册到Eureka Server
fetchRegistry: false #是否从Eureka获取注册信息,默认为true
serviceUrl:
defaultZone: http://localhost:8761/eureka/ #设置与Eureka Server交互的地址

由上面两个文件对比可以看出,在处理层级关系的时候,properties文件需要使用路径来描述层级,尽管前缀相同的路径也需要重复书写,YAML文件使用缩进的方式表示层级关系,更加清晰明了。

写这篇总结的初衷是在公司刚开始使用微服务架构的过程中,上线部署的时候由于YAML配置文件写错了而导致上线过程很不顺利,这件事一方面提醒我们YAML文件校验工具的重要性(STS自带的yedit插件就挺好用的),另一方面对于YAML语法的理解也非常重要,希望通过总结能够将YAML的语法理解得更加深入,尽量避免犯错误。

简介

YAML (YAML Ain’t Markup Language)语言(发音 /ˈjæməl/ )是一种简洁的非标记语言,YAML的设计目标,就是方便人类读写,YAML以数据为中心,使用空白、缩进、分行组织数据,使数据更加简洁易读。它实质上是一种通用的数据串行化格式。它的基本语法如下:

  • 大小写敏感

  • 使用缩进代表层级关系

  • 缩进只能使用空格,不能使用Tab

  • 缩进空格的个数没有要求,同一层级的元素左对齐即可(一般2或4个空格)

  • 使用#表示注释

  • 字符串可以不加引号进行标注

语法

学习YAML的时候可以在JS-YAML在线示例进行在线练习。

映射

YAML使用”: “(冒号加空格,记住一定要有空格)来表示每一个key: value对:

1
key: value

使用缩进代表层级关系:

1
2
3
key:
child1: value1
child2: value2

YAML还支持流式(flow)语法表示对象:

1
key: {child1: value1, child2: value2}

较为复杂的对象格式,可以使用”? “(问号加空格)代表一个复杂的key,配合”: “(冒号加空格)代表一个value:

1
2
3
4
5
6
? 
- complexkey1
- complexkey2
:
- complexvalue1
- complexvalue2

代表[complexkey1, complexkey2]: [complexvalue1, complexvalue2],也就是属性为[complexkey1, complexkey2],值为[complexvalue1, complexvalue2]

数组

用”- “(短横线加空格)表示数组的一个元素:

1
2
3
4
American:
- Boston Red Sox
- Detroit Tigers
- New York Yankees

还可以这么写:

1
2
3
4
- 
- Boston Red Sox
- Detroit Tigers
- New York Yankees

这个可以理解为:[[Boston Red Sox, Detroit Tigers, New York Yankees]],即数组元素也为数组。更加复杂的表示方法,如:

1
2
3
4
5
6
7
8
9
persons:
-
name: Mark McGwire
hr: 65
avg: 0.278
-
name: Sammy Sosa
hr: 63
avg: 0.288

其流式表示(flow style)为:

1
persons: [{name: Mark McGwire, hr: 65, avg: 0.278}, {name: Sammy Sosa, hr: 63, avg: 0.288}]

常量

YAML提供的常量包括:

  • 整数(int)

  • 浮点数(float)

  • 字符串(str)

  • NULL(null)

  • 日期(date)

  • 时间(datetime)

  • 布尔(boolean)

    参考下面的例子可以快速熟悉YAML中常量的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
booleans: 
- TRUE #使用TRUE、true、True都可以
- FALSE #使用FALSE、false、False都可以
float:
- 3.14
- 4.5632e-3 #可以使用科学计数法
int:
- 123
- 0b1010_0111_0100_1010_1110 #二进制表示
- 0o14 #十进制
- 0xC #十六进制
string:
- 哈哈
- ‘@’ #可以使用双引号或者单引号包裹特殊字符
- newline
newline2 #字符串可以拆成多行,每一行会被转化成一个空格
null:
parent: ~ #使用~表示null
date:
- 2018-12-23 #日期必须使用ISO 8601格式,即yyyy-MM-dd
datetime:
- 2018-12-23T20:02:31+08:00 #时间使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区

上述例子的JSON表示法为:

1
2
3
4
5
6
7
{ boolean: [ true, false ],
float: [ 3.14, 0.0045632 ],
int: [ 123, 42926 ],
String: [ '哈哈', '@', 'newline newline1' ],
null: [ { parent: null } ],
date: [ Sun Dec 23 2018 08:00:00 GMT+0800 (中国标准时间) ],
datetime: [ Sun Dec 23 2018 20:02:31 GMT+0800 (中国标准时间) ] }

常用特殊符号

  1. ---

    YAML可以在同一个文件中,使用---表示一个文档的开始;比如Spring Boot中application.yml的定义:

    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
    spring:
    application:
    name: microservice-discovery-eureka-ha
    ---
    spring:
    # 指定profile=peer1
    profiles: peer1
    server:
    port: 8761
    eureka:
    instance:
    # 指定当profile=peer1时,主机名是peer1
    hostname: peer1
    client:
    serviceUrl:
    # 将自己注册到peer2这个Eureka上面去
    defaultZone: http://peer2:8762/eureka/
    ---
    spring:
    profiles: peer2
    server:
    port: 8762
    eureka:
    instance:
    hostname: peer2
    client:
    serviceUrl:
    defaultZone: http://peer1:8761/eureka/

    代表定义了两个profile(peer1、peer2),也可以用来分割不同的内容,比如记录日志:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ---
    Time: 2018-12-23T19:02:31+08:00
    User: ed
    Warning:
    This is an error message for the log file
    ---
    Time: 2018-12-23T19:05:21+08:00
    User: ed
    Warning:
    A slightly different error message.
  2. ...---

    两个符号一起使用在一个配置文件中代表一个文件的结束:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    ---
    time: 20:03:20
    player: Sammy Sosa
    action: strike (miss)
    ...
    ---
    time: 20:03:47
    player: Sammy Sosa
    action: grand slam
    ...

    相当于在一个YAML文件中连续写了两个YAML配置项。

  3. !!

    使用!!进行强制类型转换:

    1
    2
    3
    string:
    - !!str 1234
    - !!str false

    允许转换的类型很多:

    1
    2
    3
    4
    5
    --- !!set
    - Mark McGwire: 65
    - Sammy Sosa: 63
    - Sammy Sosa: 63
    - Ken Griffy: 58

    set类型表示:

    1
    2
    3
    4
    5
    6
    7
    # set类型
    baseball players: !!set
    ? Mark McGwire
    ? Sammy Sosa
    ? Ken Griffey
    # 流式表示
    baseball teams: !!set { Boston Red Sox, Detroit Tigers, New York Yankees }
  4. >|

    >在字符串中折叠换行,| 保留换行符,这两个符号在YAML中较为常见,比如:

    1
    2
    3
    4
    5
    6
    accomplishment: >
    Mark set a major league
    home run record in 1998.
    stats: |
    65 Home Runs
    0.278 Batting Average

    结果为:

    1
    2
    3
    accomplishment=Mark set a major league home run record in 1998.
    stats=65 Home Runs
    0.278 Batting Average,

    注意:两个符号前面都要有空格,每行文本的前面也要有空格

    |符号常见用于在YAML中配置HTML片段:

    1
    2
    3
    4
    phraseTemplate: |
    <p style="color: red">
    some template ${msg}
    </p>
  5. 引用

    重复的内容在YAML中可以使用&来完成锚点定义,使用*来完成锚点引用,例如:

    1
    2
    3
    4
    5
    6
    hr:
    - Mark McGwire
    - &SS Sammy Sosa
    rbi:
    - *SS
    - Ken Griffey

    可以看到,在hr中,使用&SSSammy Sosa设置了一个锚点(引用),名称为SS,在rbi中,使用*SS完成了锚点使用,那么结果为:

    1
    {hr=[Mark McGwire, Sammy Sosa], rbi=[Sammy Sosa, Ken Griffey]}

    也可以这样定义:

    1
    2
    3
    4
    5
    6
    7
    SS: &SS Sammy Sosa
    hr:
    - Mark McGwire
    - *SS
    rbi:
    - *SS
    - Ken Griffey

    注意,不能独立的定义锚点,比如不能直接这样写:&SS Sammy Sosa;另外,锚点能够定义更复杂的内容,比如:

    1
    2
    3
    4
    default: &default
    - Mark McGwire
    - Sammy Sosa
    hr: *default

    hr相当于引用了default的数组。注意,hr: *default要写在同一行。

  1. <<

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    merge:
    - &CENTER { x: 1, y: 2 }
    - &LEFT { x: 0, y: 2 }
    - &BIG { r: 10 }
    - &SMALL { r: 1 }

    sample1:
    <<: *CENTER
    r: 10

    sample2:
    << : [ *CENTER, *BIG ]
    other: haha

    sample3:
    << : [ *CENTER, *BIG ]
    r: 100

    merge中,定义了四个锚点,分别在sample中使用。
    sample1中,<<: *CENTER意思是引用{x: 1,y: 2},并且合并到sample1中,那么合并的结果为:

    1
    sample1={r=10, y=2, x=1}

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

    1
    sample2={other=haha, x=1, y=2, r=10}

    sample3中,引入了*CENTER, *BIG,还使用了r: 100覆盖了引入的r: 10,所以sample3值为:

    1
    sample3={r=100, y=2, x=1}

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

参考资料

https://www.jianshu.com/p/97222440cd08

https://blog.csdn.net/vincent_hbl/article/details/75411243

https://blog.csdn.net/michaelhan3/article/details/69664932/

https://yaml.org/spec/1.2/spec.pdf

JS-YAML在线示例