GO读取Yaml文件

yaml语法

简介

YAML 语言(发音 /ˈjæməl/ )的设计目标,就是方便人类读写。它实质上是一种通用的数据串行化格式。

YAML 有一个小的怪癖。所有的 YAML 文件开始行都应该是 ---。这是 YAML 格式的一部分, 表明一个文件的开始。

基本语法规则

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进时不允许使用Tab键,只允许使用空格。
  • 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可
  • # 表示注释,从这个字符一直到行尾,都会被解析器忽略。

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

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

对象

使用冒号代表,格式为key: value。冒号后面要加一个空格:

---
#这里是注释
a: b

转为 python 如下:

{'a': 'b'}

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

--- 
host: { ip: 10.1.1.1, port: 2222 } 

转为 python如下:

{'host': {'ip': '10.1.1.1', 'port': 2222}}

或者

---
host:
  ip: 10.1.1.1
  port: 2222

也是一样的python结果

{'host': {'ip': '10.1.1.1', 'port': 2222}}

数组

列表中的所有成员都开始于相同的缩进级别, 并且使用一个 --- 作为开头(一个横杠和一个空格):

---
ipaddr:
# IP地址列表
- 120.168.117.21
- 120.168.117.22
- 120.168.117.23

转为 python 如下:

{'ipaddr': ['120.168.117.21', '120.168.117.22', '120.168.117.23']}

数据结构的子成员是一个数组,则可以在该项下面缩进一个空格。

---
-
 - source
 - destination
 - services

转为 Python 如下:

[['source', 'destination', 'services']]

数组也可以采用行内(或者流式)表示法。

---
services: [FTP, SSH]
companies: [{id: 1,name: company1,price: 200W},{id: 2,name: company2,price: 500W}]

转为 Python 如下:

{'services': ['FTP', 'SSH'], 'companies': [{'id': 1, 'name': 'company1', 'price': '200W'}, {'id': 2, 'name': 'company2', 'price': '500W'}]}

上例也可以写成

---
services:
  - FTP
  - SSH
companies:
  - id: 1
    name: company1
    price: 200W
  - id: 2
    name: company2
    price: 500W

python结果也是一样的

{'services': ['FTP', 'SSH'], 'companies': [{'id': 1, 'name': 'company1', 'price': '200W'}, {'id': 2, 'name': 'company2', 'price': '500W'}]}

对象和数组复合使用

---
languages:
 - Ruby
 - Perl
 - Python
websites:
 YAML: yaml.org
 Ruby: ruby-lang.org
 Python: python.org 

转为 Python 如下:

{'languages': ['Ruby', 'Perl', 'Python'], 'websites': {'YAML': 'yaml.org', 'Ruby': 'ruby-lang.org', 'Python': 'python.org'}}

常量

  • 字符串
  • 布尔值
  • 整数
  • 浮点数
  • 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 world'  #可以使用双引号或者单引号包裹特殊字符
  - newline
    newline2    #字符串可以拆成多行,每一行会被转化成一个空格
date:
  - 2018-02-17    #日期必须使用ISO 8601格式,即yyyy-MM-dd
datetime:
  - 2018-02-17T15:02:31+08:00    #时间使用ISO 8601格式,时间和日期之间使用T连接,最后使用+代表时区

转为 Python 如下:

{
    'boolean': [True, False],
    'float': [3.14, 685230.15],
    'int': [123, 685230],
    None: {'nodeName': 'node', 'parent': None},
    'string': ['哈哈', 'Hello world', 'newline newline2'],
    'date': [datetime.date(2018, 2, 17)],
    'datetime': [datetime.datetime(2018, 2, 17, 15, 2, 31, tzinfo=datetime.timezone(datetime.timedelta(seconds=28800)))]
}

特殊符号

1、类型转换符

YAML 允许使用两个感叹号,强制转换数据类型

---
e: !!str 123
f: !!str true

转为 Python 如下:

{'e': '123', 'f': 'true'}

2、开始结束符

---配合使用,在一个配置文件中代表一个文件的结束

---
time: 20:03:20
player: Sammy Sosa
action: strike (miss)
...
---
time: 20:03:47
player: Sammy Sosa
action: grand slam
...

3、换行符

>在字符串中折叠换行| 保留换行符,这两个符号是YAML中字符串经常使用的符号,比如:

---
this: |
  Foo
  Bar
that: >
  Foo
  Bar

转为 Python 如下:

{'this': 'Foo\nBar\n', 'that': 'Foo Bar'}

4、引用

重复的内容在YAML中可以使用&来完成锚点定义,使用*来完成锚点引用,例如:

---
defaults: &defaults
  adapter: postgres
  host: localhost

development:
  database: myapp_development
  <<: *defaults

test:
  database: myapp_test
  <<: *defaults

转为 Python 如下:

{
    'defaults': {
        'adapter': 'postgres',
        'host': 'localhost'
    },
    'development': {
        'adapter': 'postgres',
        'host': 'localhost',
        'database': 'myapp_development'
    },
    'test': {
        'adapter': 'postgres',
        'host': 'localhost',
        'database': 'myapp_test'}
}

注意,不能独立的定义锚点,比如不能直接这样写: &SS Sammy Sosa;另外,锚点能够定义更复杂的内容,比如:

---
default: &default
  - Mark McGwire
  - Sammy Sosa
hr: *default
{'default': ['Mark McGwire', 'Sammy Sosa'], 'hr': ['Mark McGwire', 'Sammy Sosa']}

那么hr相当于引用了default的数组,注意,hr: *default要写在同一行。

5、双引号

双引号不会转义字符串里面的特殊字符,例如反斜杠

---
default:
  - "\ue007"
  - '\ue007'
  - \ue007

python中的读取

{'default': ['\ue007', '\\ue007', '\\ue007']}

可以看到只有双引号原样输出了

安装使用

包的导入路径是gopkg.in/yaml.v2

要安装它,请运行:

$ go get gopkg.in/yaml.v2

API 文档

如果在浏览器中打开,导入路径本身会指向 API 文档:

yaml文件内容

# 一个稍微复杂的例子
---
apiVersion: V1
kind: Pod

abc: aaa
 
metadata:
  name: redis-stdin
spec: 
  containers: 
  - name: redis 
    image: redis
    imagePullPolicy: Always
    stdin: true
    ports:
    - containerPort: 6379
    - hostPort: 6380
  - name: mongodb 
    image: mongo:4.4.3
    imagePullPolicy: Always
    stdin: false
    ports:
    - containerPort: 27017
    - hostPort: 27017

读取yaml文件

package main

import (
	"fmt"
	"os"

	"gopkg.in/yaml.v2"
)

type Conf struct {
	ApiVersion string   `yaml:"apiVersion" json:"apiVersion"`
	Kind       string   `yaml:"kind" json:"kind"`
	Metadata   Metadata `yaml:"metadata" json:"metadata"`
	Spec       Spec     `yaml:"spec" json:"spec"`
}
type Metadata struct {
	Name string `yaml:"name" json:"name"`
}

type Spec struct {
	Containers []Containers `yaml:"containers" json:"containers"`
}
type A struct {
	Abc string `yaml:"abc" json:"abc"`
}
type Containers struct {
	Name            string              `yaml:"name" json:"name"`
	Image           string              `yaml:"image" json:"image"`
	ImagePullPolicy string              `yaml:"imagePullPolicy" json:"imagePullPolicy"`
	Stdin           string              `yaml:"stdin" json:"stdin"`
	Ports           []map[string]string `yaml:"ports" json:"ports"`
}

func main() {
    //读取全文
	file, err := os.ReadFile("1.yaml")
	if err != nil {
		fmt.Println("打开文件失败", err.Error())
		os.Exit(1)
	}
	//序列化其中大部分 可以不全,也可以多
	conf := Conf{}
	err = yaml.Unmarshal(file, &conf)
	if err != nil {
		fmt.Println(err.Error())
	}
	fmt.Printf("%+v\n", conf)
	fmt.Println(conf.Spec.Containers[1].Ports[1]["hostPort"])
	//单独序列化其中一个选项
	a := A{}
	yaml.Unmarshal(file, &a)
	fmt.Println(a.Abc)
}
posted @ 2022-11-15 10:50  厚礼蝎  阅读(892)  评论(0编辑  收藏  举报