您现在的位置是:首页 >技术教程 >golang yaml 解析问题网站首页技术教程
golang yaml 解析问题
简介golang yaml 解析问题
golang 中解析 yaml 格式内容可以使用 yaml.v3 库来解决。下载 go 依赖
go get -u gopkg.in/yaml.v3
1. 示例 yaml 数据
config_mail_template:
description: 验证码
one: Verification Code
other: Verification Code
config_mail_template_reset_code:
description: 重置密码
one: Reset password
other: Reset password
# 注释内容1
config_custom_tag: # 注释内容2
description: 自定义
one: Custom Tag
other: Custom Tag
#注释内容3
2. 普通解析
普通解析流程,解析到 map 对象,会失去对 key 的定义顺序
package yaml_demo
import (
"os"
"testing"
"gopkg.in/yaml.v3"
)
type Asset struct {
Description string `yaml:"description"`
One string `yaml:"one"`
Other string `yaml:"other"`
}
func TestParseNormal(t *testing.T) {
file, err := os.ReadFile("data.yaml")
if err != nil {
t.Error(err.Error())
return
}
var assets map[string]Asset
err = yaml.Unmarshal(file, &assets)
if err != nil {
t.Error(err.Error())
return
}
t.Log(len(assets))
}
3. 顺序解析 yaml 中的 key
yaml3 定义了 yaml.Node 对象
第一种方式
可以通过实现 UnmarshalYAML() 接口,来实现自定义对象的解析,并且保证解析 key 的顺序性
第二种方式
直接将 bytes 解析到 yaml.Node 中,此时 yaml.Node 就是文档对象
var node yaml.Node
yaml.Unmarshal(bytes,&node)
package yaml_demo
import (
"gopkg.in/yaml.v3"
"os"
"testing"
)
type Item struct {
Name string
Description string `yaml:"description"`
One string `yaml:"one"`
Other string `yaml:"other"`
}
type Items []Item
// UnmarshalYAML 自定义解析
func (a *Items) UnmarshalYAML(value *yaml.Node) error {
for i := 0; i < len(value.Content); i += 2 {
var item Item
if err := value.Content[i+1].Decode(&item); err != nil {
return err
}
item.Name = value.Content[i].Value
*a = append(*a, item)
}
return nil
}
func TestParseToSlice(t *testing.T) {
bytes, err := os.ReadFile("data.yaml")
if err != nil {
t.Error(err.Error())
return
}
var items Items
err = yaml.Unmarshal(bytes, &items)
if err != nil {
t.Error(err.Error())
return
}
t.Log(len(items))
}
4. 顺序解析后回写问题
yaml 中的 node 在更新完成后,回写内容时,虽然保留了注释,但是会去掉空白行
为了保证和原来的文件相同的空白行和注释内容,可以对 yaml 内容做如下处理
- 将 yaml 文件读取到 bytes.Buffer 中,并对其中的空白行处理,使用占位符替代空白??行,例如使用 “#placehold” 字符串,因为 “#” 是 yaml 中的注释,所以对文件内容本身没有影响
- 将 bytes.Buffer 中的内容解析成 yaml.Node 对象,并对其中需要更新的内容进行更新
- 回写时,首先使用 yaml.Marshal 将 yaml.Node 对象转换成 bytes.Buffer,然后将占位符再替换回换行符,写入文件
// 加载 yaml 文件,将空白行使用占位符替换
func loadYamlNode(file string) (*yaml.Node, error) {
dataBytes, err := os.ReadFile(file)
if err != nil {
return nil, err
}
buffer := bytes.NewBuffer(dataBytes)
storeBytes := make([]byte, 0, 2*buffer.Len())
storeBuffer := bytes.NewBuffer(storeBytes)
for {
line, err := buffer.ReadString('
')
if err != nil && err == io.EOF {
break
}
if line != "
" {
storeBuffer.WriteString(line)
} else {
storeBuffer.WriteString("#placehold
")
}
}
var dataNode yaml.Node
err = yaml.Unmarshal(storeBuffer.Bytes(), &dataNode)
if err != nil {
return nil, err
}
return &dataNode, nil
}
// 回写更新内容,将占位符使用空白行替换
func saveUpdatedContent(docNode *yaml.Node, file string) error {
var bytesData []byte
buffer := bytes.NewBuffer(bytesData)
encoder := yaml.NewEncoder(buffer)
encoder.SetIndent(2)
err := encoder.Encode(docNode)
if err != nil {
return err
}
store := make([]byte, 0, buffer.Len())
storeBuffer := bytes.NewBuffer(store)
for {
line, err := buffer.ReadString('
')
if err != nil && err == io.EOF {
break
}
if line == "#placehold
" {
storeBuffer.WriteString("
")
} else {
storeBuffer.WriteString(line)
}
}
err = os.WriteFile(file, storeBuffer.Bytes(), 0666)
return err
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。