• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

golang yaml 问题

武飞扬头像
天文学
帮助1

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 内容做如下处理

  1. 将 yaml 文件读取到 bytes.Buffer 中,并对其中的空白行处理,使用占位符替代空白??行,例如使用 “#placehold” 字符串,因为 “#” 是 yaml 中的注释,所以对文件内容本身没有影响
  2. 将 bytes.Buffer 中的内容解析成 yaml.Node 对象,并对其中需要更新的内容进行更新
  3. 回写时,首先使用 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('\n')
		if err != nil && err == io.EOF {
			break
		}
		if line != "\n" {
			storeBuffer.WriteString(line)
		} else {
			storeBuffer.WriteString("#placehold\n")
		}
	}

	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('\n')
		if err != nil && err == io.EOF {
			break
		}
		if line == "#placehold\n" {
			storeBuffer.WriteString("\n")
		} else {
			storeBuffer.WriteString(line)
		}
	}

	err = os.WriteFile(file, storeBuffer.Bytes(), 0666)
	return err
}
学新通

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgabbec
系列文章
更多 icon
同类精品
更多 icon
继续加载