赞
踩
常用的配置文件格式主要有:
下面会详细介绍并给出解析实例。
键值对是一个非常简单易用的配置文件格式。每一个键值对表示一项配置,键值对的分隔符一般使用等号或冒号。
注释以分号(;)或者井号(#)开头。
以键值对为表现形式的配置文件格式常见的有 Windows .ini 文件和 Java 中的 .properties 文件。
例如下面是一个使用键值对表示的后台服务配置。
# This is a comment
name=UserProfileServer
maxconns=1000
queuecap=10000
queuetimeout=300
loglevel=ERROR
logsize=10M
lognum=10
logpath=/usr/local/app/log
在解析上面的配置时,可以按行读取,然后放到 map 中。下面以 Go 为例,完成对上面配置文件的解析。
package main import( "bufio" "io" "os" "fmt" "errors" "strings" ) func ParseConf(confPath string) (map[string]string, error) { if confPath == ""{ return nil, errors.New("param is ill") } f, err := os.Open(confPath) if err != nil { return nil, err } defer f.Close() //store config info m := make(map[string]string) bfRd := bufio.NewReader(f) //read by line, the line terminator is '\n' for{ line, err := bfRd.ReadString('\n') if err == nil || err == io.EOF{ //ignore blank and comment line if strings.TrimSpace(line) != "" && strings.TrimSpace(line)[0] != '#'{ vKV := strings.Split(line, "=") if len(vKV) == 2 { m[vKV[0]] = vKV[1] } } if err == io.EOF{ return m, nil } } else { return nil, err } } return m, nil } func main(){ mConf, _ := ParseConf("server.kv") fmt.Println(mConf) }
运行结果:
map[loglevel:ERROR
lognum:10
logpath:/usr/local/app/log
logsize:10M
maxconns:1000
name:UserProfileServer
queuecap:10000
queuetimeout:300
]
JSON(JavaScript Object Notation) 是轻量级的文本数据交换格式,独立于语言,具有自我描述性。JSON 类似于 XML,但比 XML 更小、更快,更易解析。
JSON 语法是 JavaScript 对象表示法语法的子集。
名称/值对包括字段名称(在双引号中),后面写一个冒号,然后是值:
"firstName" : "John"
一个合法的 JSON 可以是:
JSON 对象在花括号中书写,对象可以包含多个名称/值对,使用逗号分隔:
{ "firstName":"John" , "lastName":"Doe" }
JSON 数组在方括号中书写,数组可包含多个对象:
{
"employees": [
{ "firstName":"John" , "lastName":"Doe" },
{ "firstName":"Anna" , "lastName":"Smith" },
{ "firstName":"Peter" , "lastName":"Jones" }
]
}
下面以 JSON 表示一个简单的后台服务配置:
{
"-name": "UserProfileServer",
"maxconns": "1000",
"queuecap": "10000",
"queuetimeout": "300",
"loginfo": {
"loglevel": "ERROR",
"logsize": "10M",
"lognum": "10",
"logpath": "/usr/local/app/log"
}
}
其中 -name 表示服务的名称,前面一个横杠表示该值可以转换为 XML 的标签属性。其它的键值对表示服务的各个配置项。
下面以 Go 为例,利用 Go 自带的 JSON 包 encoding/json 完成对上面服务配置的解析。
第一步,将 JSON 串转换为 Go struct。把上面的 JSON 串粘贴到 Convert JSON to Go struct。
type Server struct {
Name string `json:"-name"`
Maxconns string `json:"maxconns"`
Queuecap string `json:"queuecap"`
Queuetimeout string `json:"queuetimeout"`
Loginfo struct {
Loglevel string `json:"loglevel"`
Logsize string `json:"logsize"`
Lognum string `json:"lognum"`
Logpath string `json:"logpath"`
} `json:"loginfo"`
}
第二步,利用 Go 自带的 JSON 包 encoding/json 解析上面以 JSON 串表示的配置信息。
package main import( "encoding/json" "io/ioutil" "fmt" "os" ) func main() { file, err := os.Open("server.json") if err != nil { fmt.Printf("error:%v\n", err) return } defer file.Close() data, err := ioutil.ReadAll(file) if err != nil { fmt.Printf("error: %v", err) return } v := Server{} err = json.Unmarshal(data, &v) if err != nil { fmt.Printf("error:%v\n", err) return } fmt.Printf("%+v\n", v) }
运行输出:
{Name:UserProfileServer Maxconns:1000 Queuecap:10000 Queuetimeout:300 Loginfo:{Loglevel:ERROR Logsize:10M Lognum:10 Logpath:/usr/local/app/log}}
XML(Extensible Markup Language)是可扩展标记语言,用来传输和存储数据。因为其允许用户自定义标记名称,具有自我描述性,可灵活地用于存储服务配置信息。
XML 文档结构是一种树结构,它从“根部”开始,然后扩展到“枝叶”。XML 文档必须有一个唯一的根结点,根结点包含所有其它结点。所有结点均可拥有文本内容和属性(名称/值的对)。XML 结点也叫做 XML 元素。
编写 XML 文档时,还需要注意以下几点:
(1)所有 XML 元素都须有关闭标签;
(2)XML 标签对大小写敏感;
(3)XML 的属性值须加引号;
(4)XML 中的特殊字符可以使用实体引用来表示。在 XML 中,有 5 个预定义的实体引用:
实体引用 | 字符 | 名称 |
---|---|---|
< | < | 小于 |
> | > | 大于 |
& | & | 和号 |
' | ’ | 单引号 |
" | " | 引号 |
(5)在 XML 中编写注释的语法与 HTML 的语法很相似: |
<!-- This is a comment -->
(6)XML 元素必须遵循以下命名规则:
下面以 XML 表示一个简单的后台服务配置:
<?xml version="1.0" encoding="UTF-8"?>
<server name="UserProfileServer">
<maxconns>1000</maxconns>
<queuecap>10000</queuecap>
<queuetimeout>300</queuetimeout>
<loginfo>
<loglevel>ERROR</loglevel>
<logsize>10M</logsize>
<lognum>10</lognum>
<logpath>/usr/local/app/log</logpath>
</loginfo>
</server>
第一行是 XML 声明,它定义 XML 的版本(1.0)和所使用的编码(UTF-8)。紧接着 server 为根结点,name 为根结点的一个属性,表示服务名称,其他子结点的文本内容表示服务的具体配置项。
使用 XML 存储服务配置信息,我们如何解析呢?下面以 Go 为例,来解析上面的 XML 格式的服务配置。
第一步,将上面的 XML 配置信息粘贴到 XML to Go struct 快速获取 Go struct 的定义。
type Server struct {
Name string `xml:"name,attr"` //标签属性
Maxconns string `xml:"maxconns"`
Queuecap string `xml:"queuecap"`
Queuetimeout string `xml:"queuetimeout"`
Loginfo struct {
Loglevel string `xml:"loglevel"`
Logsize string `xml:"logsize"`
Lognum string `xml:"lognum"`
Logpath string `xml:"logpath"`
} `xml:"loginfo"`
}
第二步,借助 Go 自带的 encoding/xml 包完成 XML 文档解析。
package main import( "encoding/xml" "io/ioutil" "fmt" "os" ) func main() { file, err := os.Open("server.xml") if err != nil { fmt.Printf("error:%v\n", err) return } defer file.Close() data, err := ioutil.ReadAll(file) if err != nil { fmt.Printf("error: %v", err) return } v := Server{} err = xml.Unmarshal(data, &v) if err != nil { fmt.Printf("error:%v\n", err) return } fmt.Printf("%+v\n", v) }
运行输出:
{Name:UserProfileServer Maxconns:1000 Queuecap:10000 Queuetimeout:300 Loginfo:{Loglevel:ERROR Logsize:10M Lognum:10 Logpath:/usr/local/app/log}}
YAML(YAML Ain’t a Markup Language)是专门用来写配置文件的语言,简洁强大,相比于 JSON 和 XML,更加便于开发人员读写。
YAML 配置文件后缀为.yml 或 .yaml。
YAML 的基本语法规则如下:
---
表示开始,以三个点号...
表示结束,二者都是可选的。---
来表示指令的结束。指令是一个%后跟一个标识符和一些形参。%YAML
指定文档的 YAML 版本,%TAG
用于 tag 简写。二者都很少使用。#
表示注释,从这个字符一直到行尾,都会被解析器忽略。版本 | 发布日期 |
---|---|
YAML 1.0 | 29 January 2004 |
YAML 1.1 | 18 January 2005 |
YAML 1.2.0 | 21 July 2009 |
YAML 1.2.1 | 1 October 2009 |
YAML 1.2.2 | 1 October 2021 |
YAML 支持的数据结构有三种:
下面分别介绍这三种数据结构。
对象的一组键值对,使用冒号结构表示。
name: Steve
YAML 也允许另一种写法,将所有键值对写成一个行内对象。
who: { name: Steve, age: 18 }
当然,如果对象元素太多一行放不下,那么可以换行。
who:
name: Steve
age: 18
一组以连字符开头的行,构成一个数组。注意,连字符后需添加空格。
animals:
- Cat
- Dog
- Goldfish
连字符前可以没有缩进,也就是说下面这种写法也是 OK 的,但是还是建议缩进,因为更加易读。
animals:
- Cat
- Dog
- Goldfish
数组也可以采用行内表示法。
animal: [Cat,Dog,Goldfish]
如果数组元素是一个数组,则可以在连字符下面再缩进输入一个数组。
animals:
-
- Cat
- Dog
-
- Fish
- Goldfish
如果是行内表示,则为:
animals: [[Cat,Dog],[Fish,Goldfish]]
如果数组元素是一个对象,可以写作:
animals:
- species: dog
name: foo
- species: cat
name: bar
对应的 JSON 为:
{
"animals": [
{
"species": "dog",
"name": "foo"
},
{
"species": "cat",
"name": "bar"
}
]
}
对象和数组可以结合使用,形成复合结构。
languages:
- Ruby
- Perl
- Python
websites:
YAML: yaml.org
Ruby: ruby-lang.org
Python: python.org
Perl: use.perl.org
对应的 JSON 表示如下:
{
"languages": [
"Ruby",
"Perl",
"Python"
],
"websites": {
"YAML": "yaml.org",
"Ruby": "ruby-lang.org",
"Python": "python.org",
"Perl": "use.perl.org"
}
}
标量是最基本、不可再分的值。有以下 7 种:
使用一个例子来快速了解标量的基本使用:
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 string: - hello # 字符串默认不使用引号 - "Hello world" # 使用双引号或单引号包裹含有空格或特殊字符(如冒号)的字符串 - newline newline1 # 字符串可以拆成多行,每一换行符会被转化成一个空格 date: - 2018-02-17 # 日期必须使用 ISO 8601 格式,即 yyyy-MM-dd datetime: - 2018-02-17T15:02:31+08:00 # 时间使用 ISO 8601 格式,时间和日期之间使用 T 连接,+08:00 表示时区
YAML 字符串有三种表示方式:
字符串默认不需要引号,但是如果字符串包含空格或特殊字符(如冒号),需要加引号。
双引号字符串允许在字符串中使用转义序列来表示特殊字符,例如 \n
表示换行,\t
表示制表符,以及 \"
表示双引号。
单引号字符串被视为纯粹的字面字符串,不支持转义序列。
如果字符串含有单引号,可以使用双引号包裹,反之亦然。
锚点 & 和别名 *,可以用来完成引用。
defaults: &defaults
adapter: postgres
host: localhost
development:
database: myapp_development
<<: *defaults
等同于下面的配置。
defaults:
adapter: postgres
host: localhost
development:
database: myapp_development
adapter: postgres
host: localhost
& 用来建立锚点(defaults),<< 表示合并到当前数据,* 用来引用锚点。
如果想引入多行的文本块,可以使用 |
,|+
,|-
,>
,>+
,>-
。
|
当内容换行时,保留换行符。
如果最后一行有多个换行符,只保留一个换行符。
linefeed1: |
some
text
linefeed2: |
some
text
linefeed3: |
some
text
对应的 JSON 为:
{
"linefeed1": "some\ntext\n",
"linefeed2": "some\ntext\n",
"linefeed3": "some\n\ntext\n"
}
|+
当内容换行时,保留换行符。
与 | 的区别是,如果最后一行有多个换行符,则保留实际数目。
linefeed1: |+
some
text
linefeed2: |+
some
text
linefeed3: |+
some
text
对应的 JSON 为:
{
"linefeed1": "some\ntext\n",
"linefeed2": "some\ntext\n\n",
"linefeed3": "some\n\ntext\n"
}
|-
当内容换行时,保留换行符,但最后的换行符不保留。
linefeed1: |-
some
text
linefeed2: |-
some
text
linefeed3: |-
some
text
对应的 JSON 为:
{
"linefeed1": "some\ntext",
"linefeed2": "some\ntext",
"linefeed3": "some\n\ntext"
}
>
当内容换行时,替换为空格,但保留最后一行的换行符。
如果最后一行有多个换行符,只保留一个换行符。
linefeed1: >
some
text
linefeed2: >
some
text
linefeed3: >
some
text
对应的 JSON 为:
{
"linefeed1": "some text\n",
"linefeed2": "some text\n",
"linefeed3": "some\ntext\n"
}
>+
当内容换行时,替换为空格,但保留最后一行的换行符。
与 > 的区别是,如果最后一行有多个换行符,则保留实际数目。
linefeed1: >+
some
text
linefeed2: >+
some
text
linefeed3: >+
some
text
对应的 JSON 为:
{
"linefeed1": "some text\n",
"linefeed2": "some text\n\n",
"linefeed3": "some\ntext\n"
}
>-
(缺省行为)当内容换行时,替换为空格,不保留最后一行的换行符。
linefeed1: >-
some
text
linefeed2: >-
some
text
linefeed3: >-
some
text
对应的 JSON 为:
{
"linefeed1": "some text",
"linefeed2": "some text",
"linefeed3": "some\ntext"
}
注意:以上 6 个特殊字符,|-
和 >-
用得最多。
有时我们需要显示指定某些值的类型,可以使用 !(感叹号)显式指定类型。! 单叹号通常是自定义类型,!! 双叹号是内置类型。
# !!str 指定为字符串
string.value: !!str HelloWorld!
# !!timestamp 指定为日期时间类型
datetime.value: !!timestamp 2021-04-13T02:31:00+08:00
内置的类型如下:
!!int:整数类型
!!float:浮点类型
!!bool:布尔类型
!!str:字符串类型
!!binary:二进制类型
!!timestamp:日期时间类型
!!null:空值
!!set:集合类型
!!omap,!!pairs:键值列表或对象列表
!!seq:序列
!!map:散列表类型
一个 yaml 文件可以包含多个 yaml 文档,使用三个连字符---
分隔。
a: 10
b:
- 1
- 2
- 3
---
a: 20
b:
- 4
- 5
这种情况在 K8S 和 SpringBoot 中非常常见。
比如 SpringBoot 在一个 application.yml 文件中,通过 — 分隔多个不同配置,根据 spring.profiles.active 的值来决定启用哪个配置。
# 公共配置 spring: profiles: active: prod #使用名为 prod 的配置,这里可以切换成 dev。 datasource: url: jdbc:mysql://localhost:3306/test_db?serverTimezone=UTC&useSSL=false&allowPublicKeyRetrieval=true password: 123456 username: root --- # 开发环境配置 spring: profiles: dev #profiles属性代表配置的名称。 server: port: 8080 --- # 生产环境配置 spring: profiles: prod server: port: 80
下面以 YAML 表示一个简单的后台服务配置:
name: UserProfileServer
maxconns: 1000
queuecap: 10000
queuetimeout: 300
loginfo:
loglevel: ERROR
logsize: 10M
lognum: 10
logpath: /usr/local/app/log
因为 Go 并没有提供解析 YAML 的标准库,所以这里基于第三方开源库 go-yaml 来完成对 YAML 文件的解析。
第一步,将 YAML 配置文件的内容在 Convert YAML to Go struct 转换为 Go struct。
type Server struct {
Name string `yaml:"name"`
Maxconns int `yaml:"maxconns"`
Queuecap int `yaml:"queuecap"`
Queuetimeout int `yaml:"queuetimeout"`
Loginfo struct {
Loglevel string `yaml:"loglevel"`
Logsize string `yaml:"logsize"`
Lognum int `yaml:"lognum"`
Logpath string `yaml:"logpath"`
} `yaml:"loginfo"`
}
第二步,利用第三方开源库 go-yaml 来完成对 YAML 文件的解析。
package main import( "io/ioutil" "fmt" "os" yaml "gopkg.in/yaml.v3" ) func main() { file, err := os.Open("server.yaml") if err != nil { fmt.Printf("error:%v\n", err) return } defer file.Close() data, err := ioutil.ReadAll(file) if err != nil { fmt.Printf("error:%v\n", err) return } v := Server{} err = yaml.Unmarshal(data, &v) if err != nil { fmt.Printf("error:%v\n", err) return } fmt.Printf("%+v\n", v) }
运行输出:
{Name:UserProfileServer Maxconns:1000 Queuecap:10000 Queuetimeout:300 Loginfo:{Loglevel:ERROR Logsize:10M Lognum:10 Logpath:/usr/local/app/log}}
%YAML 1.2 --- receipt: Oz-Ware Purchase Invoice date: 2012-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 size: 8 price: 133.7 quantity: 1 bill-to: &id001 street: | 123 Tornado Alley Suite 16 city: East Centerville state: KS ship-to: *id001 specialDelivery: > Follow the Yellow Brick Road to the Emerald City. Pay no attention to the man behind the curtain. ...
注意在 YAML 中,字符串不一定要用双引号标示。另外,在缩进中空白字符的数目并不是非常重要,只要相同层次结构的元素左侧对齐就可以了(不过不能使用 TAB 字符)。
%YAML 1.2 表示版本。
这个文件的顶层由七个键值组成:其中一个键值"items",是两个元素构成的数组(或称清单),这数组中的两个元素同时也是包含了四个键值的散列表。
文件中重复的部分用这个方法处理:使用锚点(&)和引用(*)标签将"bill-to"散列表的内容复制到"ship-to"散列表。也可以在文件中加入选择性的空行,以增加可读性。
在一个文件中,可同时包含多个文件,并用---
分隔。选择性的符号...
可以用来表示文件结尾(在流通信中,这非常有用,可以在不关闭流的情况下,发送结束信号)。
面对常见配置文件格式,使用时该如何选择呢?这里给几个选择的原则:
(1)支持嵌套结构。仅仅支持 KV 结构的键值对表达能力有点弱;
(2)支持注释。不支持注释的 JSON 是给机器读的,不是给人读的;
(3)支持不同的数据类型,而不仅仅是 string。这一点,键值对和 XML 表现的非常逊色;
(4)最好支持 include 其他配置文件,方便配置模块化。复杂的配置也是无奈之举,但如果支持 include 语法,可以方便的把配置文件模块化。
通过以上几个对配置文件的要求,发现键值对不支持层级关系,JSON 不支持注释,可读性较差,虽然 XML 支持注释和层级结构,且可读性较好,但是因为起始标签一定要有个与之对应的结束标签,文件内容较大,解析时占用较多内存,传输时占用较多带宽。所以这里推荐使用 YAML 和 TOML,很多语言都有其 library 实现,跨语言不成问题。
不同系统、框架和组件可能使用自家自研的配置文件格式,因为其不具有普适性和通用性,这里就不做过多的介绍。比如 Tencent 开源 RPC 框架 TarsGo 采用了类似于 XML 的配置格式,像下面这样:
<tars>
<application>
enableset=n
<server>
node=tars.tarsnode.ServerObj@tcp -h 10.120.129.226 -p 19386 -t 60000
app=TestApp
server=HelloServer
localip=10.120.129.226
</server>
<client>
locator=tars.tarsregistry.QueryObj@tcp -h 10.120.129.226 -p 17890
sync-invoke-timeout=3000
</client>
</application>
</tars>
W3CSchool.JSON 教程
在线XML、JSON数据互转
Golang: Convert JSON to Struct
Go Package json
W3CSchool.XML 教程
Go Package xml
XML to Go struct Online Tool
The Official YAML Web Site
Convert YAML to JSON
go-yaml - Github
Convert YAML to Go struct
YAML 详解与实战-CSDN
YAML - 维基百科,自由的百科全书
Brief YAML reference — Camel 0.1.2 documentation
TOML 官网
TOML 翻译
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。