当前位置:   article > 正文

配置文件格式详解之终极无惑_axtoml_version="1.0.0" [axio] enable_long_paths=tr

axtoml_version="1.0.0" [axio] enable_long_paths=true silent = false with_

在这里插入图片描述


不管是移动应用、桌面程序还是后台服务,经常需要从配置文件中读取配置信息,进行程序初始化和改变运行时的状态。以什么样的格式来存储配置信息,这是开发人员需要面临的一个问题。

常用的配置文件格式主要有:

  • 键值对
  • JSON
  • XML
  • YAML
  • TOML

下面会详细介绍并给出解析实例。

1.键值对

键值对是一个非常简单易用的配置文件格式。每一个键值对表示一项配置,键值对的分隔符一般使用等号或冒号。

注释以分号(;)或者井号(#)开头。

以键值对为表现形式的配置文件格式常见的有 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

在解析上面的配置时,可以按行读取,然后放到 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)
}
  • 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
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

运行结果:

map[loglevel:ERROR
 lognum:10
 logpath:/usr/local/app/log
 logsize:10M
 maxconns:1000
 name:UserProfileServer
 queuecap:10000
 queuetimeout:300
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

2.JSON

JSON(JavaScript Object Notation) 是轻量级的文本数据交换格式,独立于语言,具有自我描述性。JSON 类似于 XML,但比 XML 更小、更快,更易解析。

2.1 JSON 语法

JSON 语法是 JavaScript 对象表示法语法的子集。

  • 数据在名称/值对中
  • 数据由逗号分隔
  • 花括号保存对象
  • 方括号保存数组

名称/值对包括字段名称(在双引号中),后面写一个冒号,然后是值:

"firstName" : "John"
  • 1

一个合法的 JSON 可以是:

  • 数字(整数或浮点数)
  • 字符串(在双引号中)
  • 布尔(true 或 false)
  • 数组(在方括号中)
  • 对象(在花括号中)
  • null

JSON 对象在花括号中书写,对象可以包含多个名称/值对,使用逗号分隔:

{ "firstName":"John" , "lastName":"Doe" }
  • 1

JSON 数组在方括号中书写,数组可包含多个对象:

{
"employees": [
	{ "firstName":"John" , "lastName":"Doe" },
	{ "firstName":"Anna" , "lastName":"Smith" },
	{ "firstName":"Peter" , "lastName":"Jones" }
]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

2.2 JSON 实例

下面以 JSON 表示一个简单的后台服务配置:

{
    "-name": "UserProfileServer",
    "maxconns": "1000",
    "queuecap": "10000",
    "queuetimeout": "300",
    "loginfo": {
      "loglevel": "ERROR",
      "logsize": "10M",
      "lognum": "10",
      "logpath": "/usr/local/app/log"
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

其中 -name 表示服务的名称,前面一个横杠表示该值可以转换为 XML 的标签属性。其它的键值对表示服务的各个配置项。

2.3 JSON 解析

下面以 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"`
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

第二步,利用 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)
}
  • 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

运行输出:

{Name:UserProfileServer Maxconns:1000 Queuecap:10000 Queuetimeout:300 Loginfo:{Loglevel:ERROR Logsize:10M Lognum:10 Logpath:/usr/local/app/log}}
  • 1

3.XML

XML(Extensible Markup Language)是可扩展标记语言,用来传输和存储数据。因为其允许用户自定义标记名称,具有自我描述性,可灵活地用于存储服务配置信息。

3.1 XML 语法

XML 文档结构是一种树结构,它从“根部”开始,然后扩展到“枝叶”。XML 文档必须有一个唯一的根结点,根结点包含所有其它结点。所有结点均可拥有文本内容和属性(名称/值的对)。XML 结点也叫做 XML 元素。

编写 XML 文档时,还需要注意以下几点:
(1)所有 XML 元素都须有关闭标签;
(2)XML 标签对大小写敏感;
(3)XML 的属性值须加引号;
(4)XML 中的特殊字符可以使用实体引用来表示。在 XML 中,有 5 个预定义的实体引用:

实体引用字符名称
&lt;<小于
&gt;>大于
&amp;&和号
&apos;单引号
&quot;"引号
(5)在 XML 中编写注释的语法与 HTML 的语法很相似:
<!-- This is a comment --> 
  • 1

(6)XML 元素必须遵循以下命名规则:

  • 名称可以含字母、数字以及其他的字符
  • 名称不能以数字或者标点符号开始
  • 名称不能以字符串 xml(或者 XML、Xml)开始
  • 名称不能包含空格
  • 名称不能使用保留的字词

3.2 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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

第一行是 XML 声明,它定义 XML 的版本(1.0)和所使用的编码(UTF-8)。紧接着 server 为根结点,name 为根结点的一个属性,表示服务名称,其他子结点的文本内容表示服务的具体配置项。

3.3 XML 解析

使用 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"`
} 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

第二步,借助 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)
}
  • 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

运行输出:

{Name:UserProfileServer Maxconns:1000 Queuecap:10000 Queuetimeout:300 Loginfo:{Loglevel:ERROR Logsize:10M Lognum:10 Logpath:/usr/local/app/log}}
  • 1

4.YAML

YAML(YAML Ain’t a Markup Language)是专门用来写配置文件的语言,简洁强大,相比于 JSON 和 XML,更加便于开发人员读写。

YAML 配置文件后缀为.yml 或 .yaml。

YAML 语法

YAML 的基本语法规则如下:

  • 数据结构采用键值对的形式 key: value。
  • 键冒号后面要加空格(一般为 1 个空格)。
  • 字母大小写敏感。
  • 使用缩进表示层级关系。
  • 缩进只允许使用空格,不允许使用 Tab 键。
  • 缩进空格数可以任意,只要相同层级的元素左侧对齐即可。
  • 字符串值一般不使用引号,必要时可使用。使用双引号表示字符串时,会转义字符串中的特殊字符(例如\n)。使用单引号时不会转义字符串中的特殊字符。
  • 数组中的每个元素单独一行,并以 - 开头。或使用方括号,元素用逗号隔开。注意短横杆和逗号后面都要有空格。
  • 对象中的每个成员单独一行,使用键值对形式。或者使用大括号并用逗号分开。
  • 文档以三个连字符---表示开始,以三个点号...表示结束,二者都是可选的。
  • 文档前面可能会有指令,在这种情况下,需要使用---来表示指令的结束。指令是一个%后跟一个标识符和一些形参。
  • 目前只有两个指令:%YAML指定文档的 YAML 版本,%TAG用于 tag 简写。二者都很少使用。
  • #表示注释,从这个字符一直到行尾,都会被解析器忽略。

历史版本

版本发布日期
YAML 1.029 January 2004
YAML 1.118 January 2005
YAML 1.2.021 July 2009
YAML 1.2.11 October 2009
YAML 1.2.21 October 2021

YAML 数据结构

YAML 支持的数据结构有三种:

  • 对象:键值对的集合,又称为映射(mapping)、散列(hashes)、字典(dictionary)。
  • 数组:一组按次序排列的值,又称为序列(sequence)、列表(list)。
  • 标量:单个不可再分的值

下面分别介绍这三种数据结构。

对象

对象的一组键值对,使用冒号结构表示。

name: Steve
  • 1

YAML 也允许另一种写法,将所有键值对写成一个行内对象。

who: { name: Steve, age: 18 }
  • 1

当然,如果对象元素太多一行放不下,那么可以换行。

who:
  name: Steve
  age: 18
  • 1
  • 2
  • 3

数组

一组以连字符开头的行,构成一个数组。注意,连字符后需添加空格。

animals:
  - Cat
  - Dog
  - Goldfish
  • 1
  • 2
  • 3
  • 4

连字符前可以没有缩进,也就是说下面这种写法也是 OK 的,但是还是建议缩进,因为更加易读。

animals:
- Cat
- Dog
- Goldfish
  • 1
  • 2
  • 3
  • 4

数组也可以采用行内表示法。

animal: [Cat,Dog,Goldfish]
  • 1

如果数组元素是一个数组,则可以在连字符下面再缩进输入一个数组。

animals:
  -
    - Cat
    - Dog
  -
    - Fish
    - Goldfish
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

如果是行内表示,则为:

animals: [[Cat,Dog],[Fish,Goldfish]]
  • 1

如果数组元素是一个对象,可以写作:

animals:
  - species: dog
    name: foo
  - species: cat
    name: bar
  • 1
  • 2
  • 3
  • 4
  • 5

对应的 JSON 为:

{
    "animals": [
        {
            "species": "dog",
            "name": "foo"
        },
        {
            "species": "cat",
            "name": "bar"
        }
    ]
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

复合结构

对象和数组可以结合使用,形成复合结构。

languages:
 - Ruby
 - Perl
 - Python 
websites:
 YAML: yaml.org 
 Ruby: ruby-lang.org 
 Python: python.org 
 Perl: use.perl.org 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

对应的 JSON 表示如下:

{
  "languages": [
    "Ruby",
    "Perl",
    "Python"
  ],
  "websites": {
    "YAML": "yaml.org",
    "Ruby": "ruby-lang.org",
    "Python": "python.org",
    "Perl": "use.perl.org"
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

标量

标量是最基本、不可再分的值。有以下 7 种:

  • 字符串
  • 布尔值
  • 整数
  • 浮点数
  • Null
  • 时间
  • 日期

使用一个例子来快速了解标量的基本使用:

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 表示时区
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

YAML 字符串有三种表示方式:

  • 无引号
  • 双引号
  • 单引号

字符串默认不需要引号,但是如果字符串包含空格或特殊字符(如冒号),需要加引号。

双引号字符串允许在字符串中使用转义序列来表示特殊字符,例如 \n 表示换行,\t 表示制表符,以及 \" 表示双引号。

单引号字符串被视为纯粹的字面字符串,不支持转义序列。

如果字符串含有单引号,可以使用双引号包裹,反之亦然。

引用

锚点 & 和别名 *,可以用来完成引用。

defaults: &defaults
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  <<: *defaults
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

等同于下面的配置。

defaults:
  adapter:  postgres
  host:     localhost

development:
  database: myapp_development
  adapter:  postgres
  host:     localhost
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

& 用来建立锚点(defaults),<< 表示合并到当前数据,* 用来引用锚点。

文本块

如果想引入多行的文本块,可以使用 ||+|->>+>-

  • |

当内容换行时,保留换行符。

如果最后一行有多个换行符,只保留一个换行符。

linefeed1: |
  some
  text
linefeed2: |
  some
  text
  
linefeed3: |
  some

  text
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

对应的 JSON 为:

{
    "linefeed1": "some\ntext\n",
    "linefeed2": "some\ntext\n",
    "linefeed3": "some\n\ntext\n"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • |+

当内容换行时,保留换行符。

与 | 的区别是,如果最后一行有多个换行符,则保留实际数目。

linefeed1: |+
  some
  text
linefeed2: |+
  some
  text
  
linefeed3: |+
  some

  text
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

对应的 JSON 为:

{
    "linefeed1": "some\ntext\n",
    "linefeed2": "some\ntext\n\n",
    "linefeed3": "some\n\ntext\n"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • |-

当内容换行时,保留换行符,但最后的换行符不保留。

linefeed1: |-
  some
  text
linefeed2: |-
  some
  text
  
linefeed3: |-
  some

  text
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

对应的 JSON 为:

{
    "linefeed1": "some\ntext",
    "linefeed2": "some\ntext",
    "linefeed3": "some\n\ntext"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • >

当内容换行时,替换为空格,但保留最后一行的换行符。

如果最后一行有多个换行符,只保留一个换行符。

linefeed1: >
  some
  text
linefeed2: >
  some
  text
  
linefeed3: >
  some

  text
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

对应的 JSON 为:

{
    "linefeed1": "some text\n",
    "linefeed2": "some text\n",
    "linefeed3": "some\ntext\n"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • >+

当内容换行时,替换为空格,但保留最后一行的换行符。

与 > 的区别是,如果最后一行有多个换行符,则保留实际数目。

linefeed1: >+
  some
  text
linefeed2: >+
  some
  text
  
linefeed3: >+
  some

  text
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

对应的 JSON 为:

{
    "linefeed1": "some text\n",
    "linefeed2": "some text\n\n",
    "linefeed3": "some\ntext\n"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • >-(缺省行为)

当内容换行时,替换为空格,不保留最后一行的换行符。

linefeed1: >-
  some
  text
linefeed2: >-
  some
  text
  
linefeed3: >-
  some

  text
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

对应的 JSON 为:

{
    "linefeed1": "some text",
    "linefeed2": "some text",
    "linefeed3": "some\ntext"
}
  • 1
  • 2
  • 3
  • 4
  • 5

注意:以上 6 个特殊字符,|->- 用得最多。

显示指定类型

有时我们需要显示指定某些值的类型,可以使用 !(感叹号)显式指定类型。! 单叹号通常是自定义类型,!! 双叹号是内置类型。

# !!str 指定为字符串
string.value: !!str HelloWorld!
# !!timestamp 指定为日期时间类型
datetime.value: !!timestamp 2021-04-13T02:31:00+08:00
  • 1
  • 2
  • 3
  • 4

内置的类型如下:

!!int:整数类型
!!float:浮点类型
!!bool:布尔类型
!!str:字符串类型
!!binary:二进制类型
!!timestamp:日期时间类型
!!null:空值
!!set:集合类型
!!omap,!!pairs:键值列表或对象列表
!!seq:序列
!!map:散列表类型
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

单文件多文档

一个 yaml 文件可以包含多个 yaml 文档,使用三个连字符---分隔。

a: 10
b: 
	- 1
	- 2
	- 3
---
a: 20
b: 
	- 4
	- 5 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这种情况在 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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

简单示例

下面以 YAML 表示一个简单的后台服务配置:

name: UserProfileServer
maxconns: 1000
queuecap: 10000
queuetimeout: 300
loginfo:
  loglevel: ERROR
  logsize: 10M
  lognum: 10
  logpath: /usr/local/app/log
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

YAML 解析

因为 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"`
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

第二步,利用第三方开源库 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)
}
  • 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

运行输出:

{Name:UserProfileServer Maxconns:1000 Queuecap:10000 Queuetimeout:300 Loginfo:{Loglevel:ERROR Logsize:10M Lognum:10 Logpath:/usr/local/app/log}}
  • 1

完整示例

%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.
...
  • 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

注意在 YAML 中,字符串不一定要用双引号标示。另外,在缩进中空白字符的数目并不是非常重要,只要相同层次结构的元素左侧对齐就可以了(不过不能使用 TAB 字符)。

%YAML 1.2 表示版本。

这个文件的顶层由七个键值组成:其中一个键值"items",是两个元素构成的数组(或称清单),这数组中的两个元素同时也是包含了四个键值的散列表。

文件中重复的部分用这个方法处理:使用锚点(&)和引用(*)标签将"bill-to"散列表的内容复制到"ship-to"散列表。也可以在文件中加入选择性的空行,以增加可读性。

在一个文件中,可同时包含多个文件,并用---分隔。选择性的符号...可以用来表示文件结尾(在流通信中,这非常有用,可以在不关闭流的情况下,发送结束信号)。

5.TOML

配置文件格式 TOML 快速上手

6.配置文件格式的选择

面对常见配置文件格式,使用时该如何选择呢?这里给几个选择的原则:
(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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

参考文献

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 翻译

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/453461
推荐阅读
相关标签
  

闽ICP备14008679号