Content Table

Go 使用 Yaml

使用 kubernetes-sigs/yaml 来把对象序列化为 yaml 字符串,把 yaml 字符串反序列化为 go 对象。

简单对象

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
package main
import (
"fmt"
"sigs.k8s.io/yaml"
)

type Student struct {
Id int `json:"id"` // 可以是 `yaml:"id"`,但大多数第三方 struct 使用 json,所以这里就不使用 yaml 了
Name string `json:"name`
}

func main() {
// [1] 对象序列化为 yaml 字符串
student1 := Student{1, "Alice"}
yamlBytes, _ := yaml.Marshal(student1)
yamlString := string(yamlBytes)
fmt.Println(yamlString)

// [2] yaml 字符串反序列化为对象
student2 := Student{}
err := yaml.Unmarshal([]byte(yamlString), &student2)

// yaml 有错误时报错
if (err != nil) {
fmt.Println(err)
}

fmt.Println(student2)
}

下载 yaml:

1
2
go mod init
go mod download

编译执行:

1
go run test.go

运行输出:

1
2
3
4
id: 1
name: Alice

{1 Alice}

复杂对象

很多时候,struct 的属性不会像上面的例子这样都是简单数据类型,有可能是指针、数组、指针数组、对象等等,go-yaml 能够很好的自动处理这些问题,Marshal 和 Unmarshal 的使用方法不需要任何改动,下面举例进行演示。

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
52
53
54
55
56
package main
import (
"fmt"
yaml "gopkg.in/yaml.v2"
)

type Address struct {
Street string `json:"street"`
Number int `json:"number"`
}

type School struct {
Name string `json:"name"`
Address Address `json:"address"`
}

type Student struct {
Id int `json:"id"`
Name string `json:"name`
Count *int `json:"count"` // 普通指针
School *School `json:"school"` // 对象指针
Addresses []Address `json:"addresses"` // 对象数组
}

func main() {
schoolAddress := Address{"学校的街道", 10001}
studentAddress1 := Address{"学生的地址一的街道", 20001}
studentAddress2 := Address{"学生的地址二的街道", 20002}

// [1] 复杂对象创建
student1 := Student{
Id: 1,
Name: "Alice",
School: &School{"我的学校", schoolAddress},
Addresses: []Address{studentAddress1, studentAddress2},
}
student1.Count = new(int)
*student1.Count = 10

// [2] 序列化
yamlBytes, _ := yaml.Marshal(student1)
yamlString := string(yamlBytes)
fmt.Println(yamlString)

// [3] 反序列化
student2 := Student{}
err := yaml.Unmarshal([]byte(yamlString), &student2)

if (err != nil) {
fmt.Println(err)
}

fmt.Println(student2)
fmt.Println("Count: ", *student2.Count)
fmt.Println("School: ", *student2.School)
}

运行输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
id: 1
name: Alice
count: 10
school:
name: 我的学校
address:
street: 学校的街道
number: 10001
addresses:
- street: 学生的地址一的街道
number: 20001
- street: 学生的地址二的街道
number: 20002

{1 Alice 0xc0000a28b0 0xc0000b6960 [{学生的地址一的街道 20001} {学生的地址二的街道 20002}]}
Count: 10
School: {我的学校 {学校的街道 10001}}

编程实践

在 k8s 中创建 Deployment 或者 StatefulSet 的时候,使用代码一层一层的设置属性会写的很冗长,可以使用多行字符串使用默认值定义出默认对象,然后只修改需要的属性即可,参考代码如下。

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
52
53
package main
import (
"fmt"
yaml "gopkg.in/yaml.v2"
)

type Address struct {
Street string `json:"street"`
Number int `json:"number"`
}

type School struct {
Name string `json:"name"`
Address Address `json:"address"`
}

type Student struct {
Id int `json:"id"`
Name string `json:"name`
Count *int `json:"count"` // 普通指针
School *School `json:"school"` // 对象指针
Addresses []Address `json:"addresses"` // 对象数组
}

func main() {
yamlString := `
id: 1
name: Alice
count: 10
school:
name: 我的学校
address:
street: 学校的街道
number: 10001
addresses:
- street: 学生的地址一的街道
number: 20001
- street: 学生的地址二的街道
number: 20002
`

// 反序列化
student2 := Student{}
err := yaml.Unmarshal([]byte(yamlString), &student2)

if (err != nil) {
fmt.Println(err)
}

fmt.Println(student2)
fmt.Println("Count: ", *student2.Count)
fmt.Println("School: ", *student2.School)
}