YAML 是 “YAML Ain’t a Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:”Yet Another Markup Language”(仍是一种标记语言)。
YAML是一个类似 XML、JSON 的标记性语言。YAML 强调以数据为中心,并不是以标识语言为重点。因而 YAML 本身的定义比较简单,号称“一种人性化的数据格式语言”。
yml主要用于软件配置,大有替代properties、XML、JSON的趋势,主要其格式简洁易懂
先看yml示例:
person:
name: 蒋增奎
gender: 男
spring:
profiles: dev
datasource:
url: jdbc:mysql://127.0.01/banchengbang_springboot
username: root
password: root
driver-class-name: com.mysql.jdbc.Driver
对比json
spring:{
profiles:"dev",
datasource:{
url:"jdbc:mysql://127.0.01/banchengbang_springboot",
username:"root",
password:"root",
driver-class-name:"com.mysql.jdbc.Driver"
}
}
对比java,和java对象之间的关系更接近
private String profiles;
private Datasource datasource;
- 使用缩进表示层级关系。 | json使用花括号{}
- 缩进时不允许使用 Tab 键,只允许使用空格。
- 缩进的空格数不重要,但同级元素必须左侧对齐。
- 大小写敏感
- key:value键值对模式 |和json一样
- 内容如字符串,不需要加""
与json区别
- 大小写敏感 (json 里也是大小写敏感的)
- 使用缩进表示层级关系 (json 中使用 {} 表示层级)
- "#"表示注释 (json 不允许写注释, yaml 写的配置文件要比 json 方便很多)
- key : val 值之前必须有空格
- 多个key-val组合,不需要加逗号","
总结:比json少{},逗号,多缩进和值前空格
YAML 支持以下三种数据结构:
- 对象:键值对的集合
- 数组:一组按次序排列的值
- 字面量:单个的、不可拆分的值
示例:
name: 蒋增奎 #姓名
isMan: true # 男士
birth: 2023-12-01 #出身日期
weight: 80.5
height: 172
parent: ~ # ~ 表示null
字符串可以加单引号或双引号或者没有引号。
quoted:
- 'single quoted string'
- "double quoted strings"
- withoout quoted string
都是合法的,等价于json
{
"quoted": [
"single quoted string",
"double quoted strings",
"withoout quoted string"
]
}
多行文本
字符串比较多的时候,更好的展示,选择单行/多行文本展示,用| 前缀。
例如,配置 GitHub Actions 时候运行一些命令:
# Multiline strings start with |
execute: |
npm ci
npm build
npm test
等价json
{
"execute": "npm ci\nnpm build\nnpm test\n"
}
YAML 声明空值有以下几种方法:
manager: null
blank:
tilde: ~
title: null
~: null key
等同json
{
"manager": null,
"blank": null,
"tilde": null,
"title": null,
"null": "null key"
}
时间戳表示单个时间点。它使用符号形式 ISO8601。如果未添加时区,则假定该时区为 UTC。要描述日期格式,可以省略时间部分。在这种情况下,时间默认为00:00:00Z。请参见下面示例中的用法。
time: 2020-12-07T01:02:59:34.02Z
timestamp: 2020-12-07T01:02:59:34.02 +05:30
datetime: 2020-12-07T01:02:59:34.02+05:30
notimezone: 2020-12-07T01:02:59:34.02
date: 2020-12-07
但实际转化有点问题
使用缩进表示对象与属性的层级关系。
写法1:推荐
key1: value1
key2: value2
写法2:不推荐,除非比较深的嵌套。感觉有点像json数据
key: {key1: value1, key2: value2, …}
website:
name: baidu
url: www.baidu.com
或者
website: {name: baidu,url: www.baidu.com}
写法1:推荐
YAML 使用“-”表示数组中的元素,注意:- 后面也要加空格
pets:
- cat
- dog
- pig
写法2:这种也推荐,比较简化
用[]表示 key: [value1,value2]
pets: [cat,dog,pig]
如果值一个对象如:
# java 代码:
private List<Dog> lists;
#yml数据
lists:
- name: 可乐
age: 2 #注意前面没有 - 哦
- name: 卷毛
age: 3
或者:
lists:
- {name: 可乐,age: 2}
- {name: 卷毛,age: 3}
或者:
lists: [{name: 可乐,age: 2},{name: 卷毛,age: 3}]
-
- A
- B
- C
-
- D
- E
- F
结果:[["A", "B", "C"],["D", "E", "F"]]
有对象,有数组、基本数据类型
name: zhangsan
age: 30
pets:
-dog
-cat
-pig
car:
name: QQ
child:
name: zhangxiaosan
age: 2
java代码
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Dog> lists;
private Dog dog;
private String[] arr;
}
public class Dog {
private String name;
private Integer age;
}
对应的yml填充数据
person:
boss: false
maps:
k1: v1
k2: 14
lists:
- name: d1
age: 2
- name: d2
age: 3
- {name: d3,age: 4}
birth: 2017/12/15
dog:
name: p_dog
age: 15
age: 13
last-name: 张三
arr: [s1,s2,s3]
默认情况下,YAML 会自动推断数据类型,就像 TypeScript 的类型推断一样,但是当你需要你也可以使用 标签(tags)显示指定类型,比如整数类型 两个英文感叹号 !! 再加上 int 就变成了一个整数 tag ——!!int。
# The following value should be an int, no matter what:
should-be-int: !!int 3.2
# Parse any value to string:
should-be-string: !!str 30.25
# I need the next value to be boolean:
should-be-boolean: !!bool yes
!!com.jsoft.po.Company
address: 天府大道
boss: {birth: !!date '2023-12-13', id: 1, name: 蒋增奎, weight: 78.78}
creat_date: 2000-12-31
id: 1
name: 雪州科技
users:
- {birth: !!timestamp'2023-12-13T01:10:32.464Z', id: 2, name: 张三丰, weight: !!float '89'}
- {birth: !!timestamp '2023-12-13T01:10:32.464Z', id: 3, name: 张无忌, weight: !!float '100'}
YML值相互引用的基本语法如下:
key:&anchor
value other_key:anchor
在上面的例子中,key是一个YML键,&anchor是一个锚点,value是一个值。
通过在其他地方使用anchor来引用这个值。
database:
host: &db_host localhost #给key[host]取一个别名
prot: &db_port 3306
username: &db_username root
pwd: &db_pwd 11111
development:
database:
host: *db_host # *别名,引用别名对应的值 这里就是localhost
prot: *db_port
username: *db_username
pwd: *db_pwd
实际效果如下
development:
database:
host: localhost
prot: 3306
username: root
pwd: 11111
development:
database:
host: localhost
prot: 3306
username: root
pwd: 11111
复用大节点
basic: &common #这个basic节点时可复用的
sex: 男
city: 女
user:
name: 蒋增奎
<<: *common
等价于
basic: &common #这个basic节点时可复用的
sex: 男
city: 女
user:
name: 蒋增奎
sex: 男
city: 女
& 用来建立锚点(defaults),<< 表示合并到当前数据,* 用来引用锚点。注意只是复用这个节点的数据,这个节点不会复用过来
准备两个vo类
public class Company {
private Long id;
private String name;
private String address;
private User boss;
private List<User> users;
.......
}
public class User {
private Long id;
private String name;
private Date birth;
private BigDecimal weight;
...
}
在Spring中有一个类Environment,它可以被认为是当前应用程序正在运行的环境,它继承了PropertyResolver接口,因此可以作为一个属性解析器使用。先创建一个yml文件,属性如下:
person:
name: hydra
gender: male
age: 18
使用起来也非常简单,直接使用@Autowired就可以注入到要使用的类中,然后调用它的getProperty()方法就可以根据属性名称取出对应的值了。
@RestController
public class EnvironmentController {
@Autowired
private Environment environment; //注入
@GetMapping("envTest")
private void getEnv(){
System.out.println(environment.getProperty("person.name")); //通过key获取
System.out.println(environment.getProperty("person.gender"));
Integer autoClose = environment
.getProperty("person.age", Integer.class); //类型转化
System.out.println(autoClose);
String defaultValue = environment
.getProperty("person.other", String.class, "defaultValue");//类型转化
System.out.println(defaultValue);
}
}
在Spring中还可以使用YamlPropertiesFactoryBean来读取自定义配置的yml文件,而不用再被拘束于application.yml及其激活的其他配置文件。
在使用过程中,只需要通过setResources()方法设置自定义yml配置文件的存储路径,再通过getObject()方法获取Properties对象,后续就可以通过它获取具体的属性,下面看一个例子:
@GetMapping("fcTest")
public void ymlProFctest(){
YamlPropertiesFactoryBean yamlProFb = new YamlPropertiesFactoryBean();
yamlProFb.setResources(new ClassPathResource("application2.yml"));
Properties properties = yamlProFb.getObject();
System.out.println(properties.get("person2.name"));
System.out.println(properties.get("person2.gender"));
System.out.println(properties.toString());
}
前面介绍的几种方式,在Spring环境下无需引入其他依赖就可以完成的,接下来要介绍的SnakeYml在使用前需要引入依赖,但是同时也可以脱离Spring环境单独使用。先引入依赖坐标:
<!-- https://mvnrepository.com/artifact/org.yaml/snakeyaml -->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.30</version>
</dependency>
test.yml
id: 10
title: 钢铁是怎么炼成的
price: 35
author:
firstName: 蒋
lastName: 增奎
tel: 13688006645
解析:
Yaml yaml=new Yaml();
1.返回的是一个map对象:map<key,value>
Map<String, Object> map=yaml).load(inputStream);
2.返回的是一个VO对象
VO vo=yaml).load(inputStream,VO.class);
@Test
public void t2() {
Yaml yaml=new Yaml();
Map<String, Object> map =
yaml.load(getClass().getClassLoader()
.getResourceAsStream("test.yml"));
//System.out.println(map);
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue());
}
//获得基础类型
String title=(String)map.get("title");
System.out.println("title->"+title+";id->"+map.get("id")+";price->"+map.get("price"));
//获得对象
Map<String, Object> author =(Map)map.get("author");
//获得对象的子属性
String lastName=(String) author.get("lastName");
System.out.println("lastName="+lastName);
}
效果
key = id, value = 10
key = title, value = 钢铁是怎么炼成的
key = price, value = 35
key = author, value = {firstName=蒋, lastName=增奎, tel=13688006645}
title->钢铁是怎么炼成的;id->10;price->35
lastName=增奎
把上一个步骤生成字符串,保存到company.yml文件,放置到类的根目录下
代码:
@Test
public void t2(){
Yaml yaml=new Yaml();
Company Company =
yaml.loadAs(getClass().getClassLoader().
getResourceAsStream("company.yml"), Company.class);
System.out.println(Company.toString());
}
效果
Company{
id=1, name='雪州科技', address='天府大道', boss=User{id=1,
name='蒋增奎', birth=Tue Dec 12 22:32:36 CST 2023, weight=78.78},
users=[
User{id=2, name='张三丰', birth=Tue Dec 12 22:32:36 CST 2023, weight=89},
User{id=3, name='张无忌', birth=Tue Dec 12 22:32:36 CST 2023, weight=100}
]
}
public class TestDemo {
/**
* vo生成yml格式字符串
*/
@Test
public void t1() {
Company company=getGS();
Yaml yaml = new Yaml();
StringWriter sw = new StringWriter();
yaml.dump(company, sw);
System.out.println(sw.toString());
}
public Company getGS(){
Company gs=new Company();
gs.setAddress("天府大道");
gs.setBoss(new User(1l,"蒋增奎",new Date(), new BigDecimal("78.78")));
gs.setId(1l);
gs.setName("雪州科技");
List<User> users=new ArrayList<>();
users.add(new User(2l,"张三丰",new Date(), new BigDecimal("89")));
users.add(new User(3l,"张无忌",new Date(), new BigDecimal("100")));
gs.setUsers(users);
return gs;
}
}
执行效果:
!!com.jsoft.po.Company
address: 天府大道
boss: {birth: !!timestamp '2023-12-12T23:37:08.209Z', id: 1, name: 蒋增奎, weight: 78.78}
id: 1
name: 雪州科技
users:
- {birth: !!timestamp '2023-12-12T23:37:08.211Z', id: 2, name: 张三丰, weight: !!float '89'}
- {birth: !!timestamp '2023-12-12T23:37:08.211Z', id: 3, name: 张无忌, weight: !!float '100'}
jackson不仅可以解析json,也支持yml
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.12.3</version>
</dependency>
test.yml
id: 10
title: 钢铁是怎么炼成的
price: 35
author:
firstName: 蒋
lastName: 增奎
tel: 13688006645
java代码
@Test
public void t4() throws IOException {
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
InputStream input=this.getClass().getClassLoader().getResourceAsStream("test.yml");
Map<String,Object> map = objectMapper.readValue(input, Map.class);
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println("key = " + entry.getKey() + ", value = " + entry.getValue());
}
//获得基础类型
String title=(String)map.get("title");
System.out.println("title->"+title+";id->"+map.get("id")+";price->"+map.get("price"));
//获得对象
Map<String, Object> author =(Map)map.get("author");
//获得对象的子属性
String lastName=(String) author.get("lastName");
System.out.println("lastName="+lastName);
}
效果
key = id, value = 10
key = title, value = 钢铁是怎么炼成的
key = price, value = 35
key = author, value = {firstName=蒋, lastName=增奎, tel=13688006645}
title->钢铁是怎么炼成的;id->10;price->35
lastName=增奎
把上一个步骤生成字符串,保存到company.yml文件,放置到类的根目录下
代码:
@Test
public void t5() throws IOException {
ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());
InputStream input=this.getClass().getClassLoader().getResourceAsStream("company.yml");
Company Company = objectMapper.readValue(input, Company.class);
System.out.println(Company.toString());
}
效果
Company{
id=1, name='雪州科技', address='天府大道', boss=User{id=1,
name='蒋增奎', birth=Tue Dec 12 22:32:36 CST 2023, weight=78.78},
users=[
User{id=2, name='张三丰', birth=Tue Dec 12 22:32:36 CST 2023, weight=89},
User{id=3, name='张无忌', birth=Tue Dec 12 22:32:36 CST 2023, weight=100}
]
}
看下图,从左往右是 XML、JSON、YAML 文件,JSON 通过 {} 和 [] 等简化了 XML,变得更加直观,但是当嵌套过深 XML 需要找结尾标签,JSON 需要找结尾的 },无论 XML 还是 JSON 都需要找结尾标记,很不直观,但 YAML 直接做的比 JSON 更加激进,连符号都没了,立体结构也变得扁平了,更加符合人类阅读习惯。
Java
public class Person {
private String name;
private String tel;
//-----
}
对应yml
name : jzk
tel : 13688006635
错误:
person:
name : jzk
tel : 13688006635
不要因为缩进把java类名也写入,和json一样,yml只和对象的属性值有关,和类无关