yaml 配置文件管理包
前言
YAML是专门用来写配置文件的语言,YAML的首要设计目的是为了方便人们读写,而JSON的首要设计目标是简单性和通用性。因此,YAML可以看作是JSON的自然超集,可以提高人们的可读性和更完整的信息模型,每个JSON文件也是一个有效的YAML文件。
目录
- YAML是什么
- YAML语法介绍
- R语言yaml包使用
- R语言config包使用
- 案例:ETL过程的配置管理
1. YAML是什么
YAML 是 YAML Ain’t Markup Language的缩写,是一种数据序列化语言YAML主页。YAML强调以数据为中心,设计目标包括易于阅读,为不同编程语言提供方便的数据交换,适合描述数据结构,标准接口支持通用工具,支持一次性处理,丰富的表达能力与可扩展性,易于使用。
YAML支持3种数据结构:
- 键值表,键值对的集合,包括映射,哈希,字典。
- 序列,为一组排列的值,包括数组,列表。
- 常量,为单个的不可再分隔的值,包括字符串,布尔值,整数,浮点数,Null,时间,日期
2. YAML语法介绍
由于YAML是JSON的自然超集,所以我们每个YAML语法段,都可以用JSON进行表示。本文介绍 YAML 的语法,你通过在线 JS-YAML 解析器进行验证。
1. YAML文件使用 Unicode 编码作为字符标准编码,默认为UTF-8。
2. 使用“#”来表示注释内容。
3. 使用空格作为嵌套缩进工具,通常建议使用两个空格缩进,不建议使用 tab。
4. YAML文件后缀为 .yml,如:abc.yml 。
5. YAML文件可以由一或多个文档组成,文档间使用—(三个横线)在每文档开始作为分隔符。同时,文档也可以使用…(三个点号)作为结束符。
6. 键值表:使用 :(冒号)空格表示单个键值对,使用”{}”表示一个键值表,”? ” 问号+空格表示复杂的键。当键是一个列表或键值表时,就需要使用本符号来标记。
date: 2015-02-01 # 单个键值对
items: {no: 1234, descript: cpu, price: 800.00} # 多个键值对
JSON输出
{ date: Sun Feb 01 2015 08:00:00 GMT+0800 (中国标准时间),
items: { no: 1234, descript: 'cpu', price: 800 } }
7. 序列表示:使用 -(横线)单个空格表示单个列表项, 使用”[]”表示一组数据, 组合表示。每个结构都可以嵌套组成复杂的表示结构。
7.1 序列
--- # 序列
- blue # 列表
- red
- green
JSON输出
[ 'blue', 'red', 'green' ]
7.2 嵌套结构
-
- [blue, red, green]
- [Age, Bag]
JSON输出
[ [ [ 'blue', 'red', 'green' ], [ 'Age', 'Bag' ] ] ]
7.3 复合结构对象
--- # 复合结构
languages:
- English
- 中文
- 日本語
websites:
blog: fens.me
YAML: yaml.org
R: www.r-project.org
JSON输出
{ languages: [ 'English', '中文', '日本語' ],
websites: { blog: 'fens.me', YAML: 'yaml.org', R: 'www.r-project.org' } }
8. 文本块:使用 “|” 和文本内容缩进表示的块:保留块中已有的回车换行,相当于段落块。使用 “>” 和文本内容缩进表示的块:将块中回车替换为空格,最终连接成一行。使用定界符“”(双引号)、‘’(单引号)或回车表示的块:最终表示成一行。
8.1 段落: 每行保留回车
lines: |
北京市朝阳区
ABC小区#292
JSON输出
{ lines: '北京市朝阳区\nABC小区#292\n' }
8.2 一行:最后一个回车
lines: >
北京市朝阳区
ABC小区#292
JSON输出
{ lines: '北京市朝阳区 ABC小区#292\n' }
8.3 连续一行:无回车
lines:
“北京市朝阳区
ABC小区#292”
JSON输出
{ lines: '“北京市朝阳区 ABC小区#292”' }
9. 数据类型:整数,浮点数,字符串,NULL,日期,布尔,时间。
--- # 数据类型
integer: 12345 # 整数标准形式
octal: 0o34 # 八进制表示,第二个是字母 o
hex: 0xFF # 十六进制表示
float: 1.23e+3 # 浮点数
fixed: 13.67 # 固定小数
minmin: -.inf # 表示负无穷
notNumber: .NaN # 无效数字
null: # 空值
boolean: [true, false] # 布尔值
string: '12345' # 字符串
date: 2015-08-23 # 日期
datetime: 2015-08-23T02:02:00.1z # 日期时间
iso8601: 2015-08-23t21:59:43.10-05:00 # iso8601 日期格式
spaced: 2015-08-23 21:59:43.10 -5 # ?
JSON输出
{ integer: 12345,
octal: '0o34',
hex: 255,
float: 1230,
fixed: 13.67,
minmin: -Infinity,
notNumber: NaN,
null: null,
boolean: [ true, false ],
string: '12345',
date: Sun Aug 23 2015 08:00:00 GMT+0800 (中国标准时间),
datetime: '2015-08-23T02:02:00.1z',
iso8601: Mon Aug 24 2015 10:59:43 GMT+0800 (中国标准时间),
spaced: Mon Aug 24 2015 10:59:43 GMT+0800 (中国标准时间) }
10. 特殊字符:!(叹号),!!(双叹号)
--- # 特殊字符
isString: !!str 2015-08-23 # 强调是字符串不是日期数据
picture: !!binary | # Base64 图片
R0lGODlhDAAMAIQAAP//9/X
17unp5WZmZgAAAOfn515eXv
Pz7Y6OjuDg4J+fn5OTk6enp
56enmleECcgggoBADs=
JSON输出
{ isString: '2015-08-23',
picture:
[ 71,
73,
70,
56,
57,
97,
12,
0,
12,
0,
132,
0,
0,
255,
255,
247,
245,
245,
238,
233,
233,
229,
102,
102,
102,
0,
0,
0,
231,
231,
231,
94,
94,
94,
243,
243,
237,
142,
142,
142,
224,
224,
224,
159,
159,
159,
147,
147,
147,
167,
167,
167,
158,
158,
158,
105,
94,
16,
39,
32,
130,
10,
1,
0,
59 ] }
11. 下面是内置类型
!!int # 整数类型
!!float # 浮点类型
!!bool # 布尔类型
!!str # 字符串类型
!!binary # 也是字符串类型
!!timestamp # 日期时间类型
!!null # 空值
!!set # 集合
!!omap, !!pairs # 键值列表或对象列表
!!seq # 序列,也是列表
!!map # 键值表
11.1 omap
--- !!omap
- Mark: 65
- Sammy: 63
- Key: 58
JSON输出
<pre style="margin: 0pt 0pt 15px; padding: 10px; background: none 0% 0% repeat scroll rgb(249, 249, 212); overflow: auto;">[ { Mark: 65 }, { Sammy: 63 }, { Key: 58 } ]</pre>
11.2 set
--- !!set # 注意,“?”表示键为列表,在这里列表为 null
? Mark
? Sammy
? Key
JSON输出
<pre style="margin: 0pt 0pt 15px; padding: 10px; background: none 0% 0% repeat scroll rgb(249, 249, 212); overflow: auto;">{ Mark: null, Sammy: null, Key: null }
</pre>
12. 锚点与引用:使用 “&” 定义数据锚点(即要复制的数据),使用 “*” 引用上述锚点数据(即数据的复制目的地)。
hr:
- Mark McGwire
# Following node labeled SS
- &SS 定义要复制的数据
rbi:
- *SS # Subsequent occurrence 这里是数据复制目标
- Ken Griffey
JSON输出
{ hr: [ 'Mark McGwire', '定义要复制的数据' ],
rbi: [ '定义要复制的数据', 'Ken Griffey' ] }
YAML 语法结构还是挺简单的,主要是3个数据结构类型的组合使用。我们理解了核心语法后,接下来就可以使用R语言来调用YAML了,进行在R语言中的配置管理。
3. R语言yaml包使用
R语言中,有多个包都可以进行YAML的数据解析,我来介绍2个包一个是yaml包,另一个是config包,下面我们分别介绍一下。2个包都能解析YAML文件,但设计目标是不同的。
- yaml包,是专门用来解析YAML语法的包,核心功能就是解析YAML文件到R对象,再把R对象生成YAML文件。
- config包,是专门用来进行配置管理的。用于项目配置管理的场景,当开发,测试和生产环境同一个参数需要配置不同的值,使用config包使用起来比yaml包更简单。
开发所使用的系统环境
- Win10 64bit
- R: 3.6.1 x86_64-w64-mingw32/x64 b4bit
先让我来学习一下yaml包,yaml包安装和使用,都很容易。首先,我们先进行安装。
> install.packages("yaml")
> library(yaml)
查看yaml包,发现一共就5个函数
- read_yaml,读取一个YAML文件
- write_yaml,写入到一个YAML文件
- as.yaml,转换R对象为YAML字符串
- yaml.load,转换YAML字符串到R对象
- yaml.load_file,读文件转换YAML到R对象,看了一个代码和read_yaml()函数没有什么区别
接下来,我们新建一个abc.yml文件,用于描述一个订单。
invoice: 31223
date : 2020-01-23
bill-to: &id001
given : 小明
address:
lines: |
北京市朝阳区
ABC小区 #292
city : 北京
ship-to: *id001
product:
- sku : BL394D
quantity : 4
price : 450.00
- sku : BL4438H
quantity : 1
price : 2392.00
tax : 251.42
total: 4443.52
使用yaml包,来加载这个文件。
> abc<-read_yaml("abc.yml",fileEncoding = "UTF-8")
查看R语言结构,就是映射为R语言中的list结构。
[图片上传失败...(image-c6ce79-1589298966859)]
在R语言中,修改结果。
# 修改日期
abc$date<- '2019-01-01'
# 去掉第二件商品
abc$product[2]<-NA
输出保存为新的文件abc2.yml
write_yaml(abc,"abc2.yml",fileEncoding = "UTF-8")
打开文件abc2.yml
invoice: 31223
date: '2019-01-01'
bill-to:
given: 小明
address:
lines: |
北京市朝阳区
ABC小区 #292
city: 北京
ship-to:
given: 小明
address:
lines: |
北京市朝阳区
ABC小区 #292
city: 北京
product:
- sku: BL394D
quantity: 4
price: 450.0
- .na
tax: 251.42
total: 4443.52
我们可以进行JSON转换,再通过JSON的格式进行观察。在R语言中JSON和R的详细介绍,请参考文章R和JSON的傻瓜式编程
# 加载JSON库
> library(RJSONIO)
> abcj<-toJSON(abc)
# 查看JSON
> cat(abcj)
{
"invoice": 31223,
"date": "2019-01-01",
"bill-to": {
"given": "小明",
"address": {
"lines": "北京市朝阳区\nABC小区 #292\n",
"city": "北京"
}
},
"ship-to": {
"given": "小明",
"address": {
"lines": "北京市朝阳区\nABC小区 #292\n",
"city": "北京"
}
},
"product": [
{
"sku": "BL394D",
"quantity": 4,
"price": 450
},
null
],
"tax": 251.42,
"total": 4443.52
}
4. R语言config包使用
config包,是RStudio公司开发的,是专门用来进行配置管理的。用于项目配置管理的场景,区别开发,测试和生产环境可能需要不同的值,使用起来比yaml更简单。
首先是安装。
> install.packages("config")
> library(config)
使用config包,我们新一个datasource.yml。用来描述,在软件项目开发时,开发环境(default),测试环境(test),生产环境(production)对于datafile这个参数,会用于不同的文件。
# 开发环境
default:
time: 5
datafile: "data-dev.csv"
# 测试环境
test:
time: 30
datafile: "data-test.csv"
# 生产环境
production:
inherits: test
datafile: "data-prod.csv"
使用config包读取datasource.yml。
# 默认情况会加载default配置
> dev <- config::get(file = "datasource.yml")
> dev
$time
[1] 5
$datafile
[1] "data-dev.csv"
attr(,"config")
[1] "default"
attr(,"file")
[1] "c:\\work\\R\\config\\datasource.yml"
当我们在生产环境时,可以在R的运行时环境配置好全局的环境变量,这个时候就可以直接取对prod的配置信息了。
# 设置全局变量
> Sys.setenv(R_CONFIG_ACTIVE = "production")
# 判断是否production被激活
> config::is_active("production")
[1] TRUE
# 获得配置
> prod <- config::get(file = "datasource.yml")
> prod
$time
[1] 30
$inherits
[1] "test"
$datafile
[1] "data-prod.csv"
attr(,"config")
[1] "production"
attr(,"file")
[1] "c:\\work\\R\\config\\datasource.yml"
所以,我们看到config包,提供了对于yaml的在软件管理的场景的使用方法,很简单,更方便。
5. 案例:ETL过程的配置管理
现在,我们已经完全了解了yaml的语法,以及程序如何调用的使用方法了,那么接下来就是真正地发挥YAML的作用了,把YAML的配置管理用于具体的业务逻辑中。
我设计了一个真实的场景,做数据项目的时候,经常需要对数据进行各种变形处理,就是常说的ETL过程。先加载数据,然后数据处理,最后输出数据,进行可视化。
image数据的连续处理过程,在这个思路上其实也出现了一大批软件,通过界面拖拽就实现的功能操作。从软件设计的角度来说,数据连续处理的过程,是非常适合用配置管理的。以配置的方法简化了软件开发的复杂度,又能让用户自己参与其中,用户体验那叫一个爽。
我们就可以用YAML格式,来做为不同软件的核心数据交换结构。基于这个文字型的数据结构,用户可以自己编写,因为易读易懂。同时,基于这个文字型的数据结构,程序可以解析关键的配置信息,这样就打通人和程序的对话格式。把原本看起来很复杂的过程,进行了抽象,便利地实现,用户可参与的配置任务。
我设计一个task.yml的文件,用于描述一个数据处理的过程。
version: v1.0 # 定义版本
resource: # 数据源
- database: &mysql1
dbname: yaml
host: localhost
port: 3306
username: yaml
password: yaml
- file: &file1
path: C:/work/demo/test.csv
file_encoding: 'utf-8'
data_input: # 数据读取
method: mysql # 数据读取方法,格式为 method: 方法名
dataref: *mysql1
param:
table_name: iris_table
columns: Sepal.Length
data_analysis:
method: mean # 分析方法,格式为 method: 方法名
param: # 参数
na_rm: FALSE # 具体参数格式 参数名: 参数值
data_output:
method: csv # 数据输出方法,格式为 method: 方法名
dataref: *file1
配置解释:
- version:定义配置文件版本
- resource:定义数据源,数据库,文件等
- data_input:定义数据数据输入,引用数据源
- data_analysis: 定义数据处理方法,计算平均值
- data_output:定义数据数据输出,引用数据源
运行R语言的代码,就可以执行上面的配置过程,下面的代码是只是一个代码结构,涉及到很多具体的函数调用细节,等下次在详细介绍。
library(magrittr)
task<-read_yaml("task.yml",fileEncoding = "UTF-8")
task
run<-function(task){
data_input<-function(obj){} #数据加载
data_analysis<-function(dat,obj){} #数据处理
data_output<-function(dat,obj){} #数据输出
data_input(task$data_input) %>%
data_analysis(a,task$data_analysis) %>%
data_output(task$data_output)
}
# 运行函数
run(task)
本文对YAML进行比较完整的介绍,配置管理工作是软件开发中非常重要的,现在工具越来越便利,把程序员可以从大量复杂的场景剥离出来,只专注于代码本身,可以极大地提高开发效率,并保证程序的质量。把YAML试试用起来吧。
网友评论