YAML使用

[本文(YAML使用)原始地址]http://xcoder.me/2017-05/language/YAML使用/

img

YAML是“另一种标记语言”的外语缩写,但为了强调这种语言以数据做为中心,而不是以置标语言为重点,而用返璞词重新命名。它是一种直观的能够被电脑识别的数据序列化格式,是一个可读性高并且容易被人类阅读,容易和脚本语言交互,用来表达资料序列的编程语言。它是类似于标准通用标记语言的子集XML的数据描述语言,语法比XML简单很多。

YAML vs XML

与YAML相似的数据格式定义语言是XML,YAML比XML优越性表现在

优势:

  • YAML的可读性好
  • YAML和脚本语言的交互性好
  • YAML使用实现语言的数据类型
  • YAML有一个一致的信息模型
  • YAML易于实现

上面5条是XML不足的地方,同时,YAML也具有XML的下列优点:

  • YAML可以基于流来处理
  • YAML表达能力强,扩展性好

YAML类似于XML的数据描述语言,语法比XML简单很多,YAML试图用一种比XML更敏捷的方式,来完成XML所完成的任务。

YAML vs JSON

JSON的语法其实是YAML的子集,大部分的JSON文件都可以被YAML的剖析器剖析。虽然大部分的数据分层形式也可以使用类似JSON的格式,不过YAML并不建议这样使用,除非这样编写能让文件可读性增加,更重要的是,YAML的许多扩展在JSON是找不到的,如:进阶资料形态关系锚点字串不需要引号映射资料形态会储存键值的顺序等。

YAML用途

脚本语言

由于实现简单,解析成本很低,YAML特别适合在脚本语言中使用。列一下现有的语言实现:Ruby,Java,Perl,Python,PHP,OCaml,JavaScript,除了Java,其他都是脚本语言。

序列化

YAML比较适合做序列化。因为它是宿主语言数据类型直转的。

配置文件

YAML做配置文件也不错。写YAML要比写XML快得多(无需关注标签或引号),并且比ini文档功能更强。

调试

由于其很强的阅读性,用于调试过程中dump出信息供分析也是一种比较方便的做法。

YAML缺陷与不足

YAML没有自己的数据类型的定义,而是使用实现语言的数据类型。一个YAML文件,在不同语言中解析后得到的数据类型可能会不同,由于其兼容性问题,不同语言间的数据流转不建议使用YAML。

YAML语法与范例

  • YAML使用可打印的Unicode字符,可使用UTF-8或UTF-16
  • 使用空白字符(不能使用Tab)分层,同层元素左侧对齐
  • 单行注解由井字号( # )开始,可以出现在行中任何位置
  • 每个清单成员以单行表示,并用短杠+空白(- )起始
  • 每个杂凑表的成员用冒号+空白(: )分开键和值
  • 杂凑表的键值可以用问号 (?)起始,表示多个词汇组成的键值
  • 字串一般不使用引号,但必要的时候可以用引号框住
  • 使用双引号表示字串时,可用倒斜线(\)进行特殊字符转义
  • 区块的字串用缩排和修饰词(非必要)来和其他资料分隔,有新行保留(使用符号|)或新行折叠(使用符号>)两种方式
  • 在单一档案中,可用连续三个连字号()区分多个档案
  • 可选择性的连续三个点号()用来表示档案结尾(在流式传输时非常有用,不需要关闭流即可知道到达结尾处)
  • 重复的内容可使从参考标记星号 (*)复制到锚点标记(&
  • 指定格式可以使用两个惊叹号 ( !! ),后面接上名称
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
receipt: Oz-Ware Purchase Invoice
date: 2007-08-06
customer:
given: Dorothy
family: Gale
items:
- part_no: A4786
descrip: Water Bucket (Filled)
price: 1.47
quantity: 4
- part_no: E1628
descrip: High Heeled "Ruby" Slippers
price: 100.27
quantity: 1
bill-to: &id001
street: |
123 Tornado Alley
Suite 16
city: East Westville
state: KS
ship-to: *id001
specialDelivery: >
Follow the Yellow Brick
Road to the Emerald City.
Pay no attention to the
man behind the curtain.
...

这个文件的的顶层由七个键值组成:其中一个键值”items”,是个两个元素构成的清单,清单中的两个元素同时也是包含了四个键值的杂凑表。
文件中重复的部分处理方式:使用锚点(&)和参考(*)标签将”bill-to”杂凑表的内容复制到”ship-to”杂凑表。也可以在文件中加入选择性的空行,以增加可读性。

YAML的JavaScript实现

阮一峰的网络日志

YAML的JAVA实现

YAML已经有了多种语言不少实现,详见YAML官网
一般YAML文件扩展名为.yaml,比如John.yaml,其内容为:

1
2
3
4
5
6
7
8
9
10
name: John Smith
age: 37
children:
- name: Jimmy Smith
age: 15
- name: Jenny Smith
age: 12
spouse:
name: Jane Smith
age: 25

由于yaml的超强可读性,我们了解到:John今年37岁,两个孩子Jimmy 和Jenny活泼可爱,妻子Jane年轻美貌,而且年仅25岁,一个幸福的四口之家。
对John.yaml进行java描述,抽象出一个Person类,如下:

1
2
3
4
5
6
7
public class Person {
private String name;
private int age;
private Person sponse;
private Person[] children;
// setXXX, getXXX方法略.
}

现在我们使用java装配一个Jone:

1
2
3
4
5
6
7
8
9
10
11
12
13
Person john = new Person();
john.setAge(37);
john.setName("John Smith");
Person sponse = new Person();
sponse.setName("Jane Smith");
sponse.setAge(25);
john.setSponse(sponse);
Person[] children = {new Person(), new Person()};
children[0].setName("Jimmy Smith");
children[0].setAge(15);
children[1].setName("Jenny Smith");
children[1].setAge(12);
john.setChildren(children);

使用SnakeYAML实现

项目主页:http://code.google.com/p/snakeyaml/
使用手册:https://code.google.com/p/snakeyaml/wiki/Documentation
SnakeYAML是一个标准的YAML的java实现,它有以下特点:

  • 完全支持YAML 1.1,可以跑通规范中的所有示例
  • 支持YAML的所有类型
  • 支持UTF-8/UTF-16的输入和输出
  • 提供了本地java对象的序列化和反序列化的高层API
  • 提供相对合理的错误提示信息

使用SnakeYAML将john dump出来,如果有引用相同对象,则dump出到yaml文件会自动使用&*进行锚点和引用

1
2
3
4
5
6
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK);
Yaml yaml = new Yaml(options);
//Yaml yaml = new Yaml();
String dump = yaml.dump(john);
System.out.println(dump);

内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
!!Person
age: 37
children:
- age: 15
children: null
name: Jimmy Smith
sponse: null
- age: 12
children: null
name: Jenny Smith
sponse: null
name: John Smith
sponse:
age: 25
children: null
name: Jane Smith
sponse: null

现在用SnakeYAML把yaml load进来,如果yaml文件中使用了&*,则会自动对load出来的对象赋相同的值

1
2
3
4
Yaml yaml = new Yaml();
Object load = yaml.load(new FileInputStream(new File("jhon.yaml")));
System.out.println(load.getClass());
System.out.println(yaml.dump(load));


1
2
3
Yaml yaml = new Yaml(options);
Person person = yaml.loadAs(inputStream, Person.class);
System.out.println(person.getSponse().getChildren().length);

如果一个yaml文件中有多个文档,由分割,解析如下:

1
2
3
4
5
6
Yaml yaml = new Yaml();
int counter = 0;
for (Object data : yaml.loadAll(input)) {
System.out.println(data);
counter++;
}

保存一个Map对象:

1
2
3
4
5
6
7
8
9
10
11
Map<String, Object> data = new HashMap<String, Object>();
data.put("name", "Silenthand Olleander");
data.put("race", "Human");
data.put("traits", new String[] { "ONE_HAND", "ONE_EYE" });
Yaml yaml = new Yaml();
String output = yaml.dump(data);
System.out.println(output);
// or
StringWriter writer = new StringWriter();
yaml.dump(data, writer);
System.out.println(writer.toString());

将多个文档dump出到同一个yaml文件中去:

1
2
3
4
5
6
7
8
9
10
List<Integer> docs = new LinkedList<Integer>();
for (int i = 1; i < 4; i++) {
docs.add(i);
}
DumperOptions options = new DumperOptions();
//options.setCanonical(true);
options.explicitStart(true);
Yaml yaml = new Yaml(options);
System.out.println(yaml.dump(docs));
System.out.println(yaml.dumpAll(docs.iterator()));

1
2
3
4
5
--- [1, 2, 3]
--- 1
--- 2
--- 3

YAML与java类型对照表:
YAML | JAVA
——– | ——-
!null |null
!!bool |Boolean
!!int |Integer, Long, BigInteger
!!float |Double
!!binary |String
!!timestamp |java.util.Date, java.sql.Date, java.sql.Timestamp
!!omap, !!pairs |List of Object[]
!!set |Set
!!str |String
!!seq |List
!!map |Map

集合的默认实现是:

  • List: ArrayList
  • Map: LinkedHashMap

YAML转MAP

YAML数据

1
2
3
4
5
6
7
8
9
data1: AAA
data2:
- aaa
- bbb
- ccc
data3:
zzz: ZZZ
yyy: YYY
xxx: XXX

JAVA代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Yaml sYaml = new Yaml();
@SuppressWarnings("unchecked")
Map<String, Object> yaml = (Map<String, Object>) sYaml.load(yamlString);
assertThat((String) yaml.get("data1"), is("AAA"));
@SuppressWarnings("unchecked")
List<String> data2 = (List<String>) yaml.get("data2");
assertThat(data2.get(0), is("aaa"));
assertThat(data2.get(1), is("bbb"));
assertThat(data2.get(2), is("ccc"));
@SuppressWarnings("unchecked")
Map<String, String> data3 = (Map<String, String>) yaml.get("data3");
assertThat(data3.get("zzz"), is("ZZZ"));
assertThat(data3.get("yyy"), is("YYY"));
assertThat(data3.get("xxx"), is("XXX"));

YAML转Bean

1
2
3
4
5
6
7
8
9
10
!!com.YamlBean
data1: AAA
data2:
- aaa
- bbb
- ccc
data3:
zzz: ZZZ
yyy: YYY
xxx: XXX
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
package com;
import java.util.List;
import java.util.Map;
public class YamlBean {
private String data1;
private List<String> data2;
private Map<String, String> data3;
public String getData1() {
return data1;
}
public void setData1(String data1) {
this.data1 = data1;
}
public List<String> getData2() {
return data2;
}
public void setData2(List<String> data2) {
this.data2 = data2;
}
public Map<String, String> getData3() {
return data3;
}
public void setData3(Map<String, String> data3) {
this.data3 = data3;
}
}
1
2
3
4
5
6
7
8
9
10
Yaml yaml = new Yaml();
YamlBean yamlBean = (YamlBean) yaml.load(yamlString);
assertThat(yamlBean.getData1(), is("AAA"));
assertThat(yamlBean.getData2().get(0), is("aaa"));
assertThat(yamlBean.getData2().get(1), is("bbb"));
assertThat(yamlBean.getData2().get(2), is("ccc"));
assertThat(yamlBean.getData3().get("zzz"), is("ZZZ"));
assertThat(yamlBean.getData3().get("yyy"), is("YYY"));
assertThat(yamlBean.getData3().get("xxx"), is("XXX"));
1
2
3
4
5
6
7
8
9
10
Yaml yaml = new Yaml();
YamlBean yamlBean = yaml.loadAs(yamlString, YamlBean.class);
assertThat(yamlBean.getData1(), is("AAA"));
assertThat(yamlBean.getData2().get(0), is("aaa"));
assertThat(yamlBean.getData2().get(1), is("bbb"));
assertThat(yamlBean.getData2().get(2), is("ccc"));
assertThat(yamlBean.getData3().get("zzz"), is("ZZZ"));
assertThat(yamlBean.getData3().get("yyy"), is("YYY"));
assertThat(yamlBean.getData3().get("xxx"), is("XXX"));
小英雄雨来 wechat
扫码二维码或搜索"架构演进之旅"订阅微信公众号
enjoy?donate!