ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Golang yaml与toml解析

2021-06-08 16:04:46  阅读:218  来源: 互联网

标签:struct err Golang yaml toml config string


yaml与toml是当前流行度较高的两种配置文件类型,其解析方式也非常类似,因此本文将他们合在一起讲。

go-yaml/yaml: YAML support for the Go language. (github.com)

BurntSushi/toml: TOML parser for Golang with reflection. (github.com)

pelletier/go-toml: Go library for the TOML file format (github.com)

第一个是go yaml解析库,后两个是toml解析库,但BurntSushi的toml解析库已经不再维护,所以推荐使用下面的go-toml,参考:

toml · pkg.go.dev

yaml · pkg.go.dev

一、YAML
package main

import (
	"fmt"
	"log"
	"gopkg.in/yaml.v2"
)

var data = `
a: Easy!
b:
  c: 2
  d: [3, 4]
`

// Note: struct fields must be public in order for unmarshal to correctly populate the data.
type Config struct {
	A string
	B struct {
		RenamedC int   `yaml:"c"`
		D	   []int
	}
}

func main() {
	config := new(Config)

	//unmarshal bytes to config
	err := yaml.Unmarshal([]byte(data), config)
	if err != nil {
		log.Fatalln(err)
	}
	fmt.Printf("--- config loads:\n%v\n\n", config)

	//marshal config to bytes
	configBytes, err := yaml.Marshal(config)
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	fmt.Printf("--- config dumps:\n%s\n\n", string(configBytes))

	m := make(map[interface{}]interface{})

	//unmarshal bytes to map
	err = yaml.Unmarshal([]byte(data), &m)
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	fmt.Printf("--- map loads:\n%v\n\n", m)

	//marshal map to bytes
	d, err := yaml.Marshal(&m)
	if err != nil {
		log.Fatalf("error: %v", err)
	}
	fmt.Printf("--- map dumps:\n%s\n\n", string(d))
}

同json标准库相似,yaml.Encoder与yaml.Decoder负责对接文件类型的yaml的读写,我们只需要os.OpenFile创建或打开一个文件,就可以直接写入或读取其中的yaml配置啦:

有如下一个yaml文件:

apiVersion: pingcap.com/v1alpha1
kind: TidbCluster
metadata:
  name: my-test-cluster
  namespace: tidb-cluster
package main

import (
	"fmt"
	"log"
	"gopkg.in/yaml.v2"
	"os"
)

func checkErr(err error){
	if err != nil{
		log.Fatalln(err)
	}
}

type Config struct {
	ApiVersion 	string	`yaml:"apiVersion"`  //yaml解析时会默认把字段解析为全小写来匹配,如果配置文件中使用驼峰规则那么这里就需要使用annotations主动标识
	Kind		string
	Metadata struct{
		Name		string
		Namespace	string
	}
}

func main() {
	config := new(Config)

	readFile, err := os.OpenFile("main.yaml", os.O_RDWR, 0644)
	checkErr(err)
	defer readFile.Close()

	decoder := yaml.NewDecoder(readFile)
	decoder.Decode(config)

	fmt.Printf("apiversion:%s, kind: %s, meta.name:%s, meta.namespace: %s", config.ApiVersion, config.Kind,
		config.Metadata.Name, config.Metadata.Namespace)

	writeFile, err := os.OpenFile("main1.yaml", os.O_CREATE|os.O_RDWR, 0644)
	checkErr(err)
	defer writeFile.Close()

	encoder := yaml.NewEncoder(writeFile)
	defer encoder.Close()
	encoder.Encode(config)
}
//需要特别注意的一点是:
//如果Encoder.Encode(v interface{})的输入不是标准化输入(即struct或map等格式化的输入),而是string,那么写入的文件内容就会变成透传yaml的格式(有一个|2的标志开头),这种生成的yaml文件很难解析,建议如果是从string获取yaml配置,那么先Unmarshal反序列化为标准化格式(map or struct),然后使用此格式Decode生成配置文件

二、TOML解析

toml与yaml以及json的解析也基本一样,不同的是toml库中提供了大量的其他辅助type struct,例如可以不用struct和map来存储yaml的解析结果,我们可以使用toml.Tree类型来存储toml解析结果以方便访问,使用Tree类型的构造方法来获取一个Tree然后使用其type method来访问树的元素即可。

这里先讲述使用传统方式解析toml文件,然后再示例如何使用Tree类型来更便捷的处理toml。

假设我们有一个如下main.toml文件:

[app]
name = "myapp"
[app.log]
max_size = "100MB"
max_files = 3
[app.resource]
cpu = "1000m"
memory = "1GB"
[others]
foo = "foo~"

我们读取toml文件并修改其中max_files的值为4,然后重新写入另一个名为main.toml.new的文件中:

package main

import (
	"fmt"
	"log"
	"os"
	toml "github.com/pelletier/go-toml"
)

func checkErr(err error){
	if err != nil{
		log.Fatalln(err)
	}
}
//目前toml库与yaml相比有一个弱点:yaml在将config struct写入文件时会自动把配置项的首字母重新转回小写,但toml不会,所以为了统一前后格式,这里把toml的annotations都加上
type config struct {
	App struct{
		Name string	`toml:"name"`
		Log struct{
			MaxSize string	`toml:"max_size"`
			MaxFiles uint8	`toml:"max_files"`
		}	`toml:"log"`
		Resource struct{
			Cpu string	`toml:"cpu"`
			Memory string	`toml:"memory"`
		}	`toml:"resource"`
	}	`toml:"app"`
	Others struct{
		Foo string	`toml:"foo"`
	}	`toml:"others"`
}

func main() {
	config := new(config)
	readFile, err := os.OpenFile("main.toml", os.O_RDWR, 0644)
	checkErr(err)
	defer readFile.Close()

	decoder := toml.NewDecoder(readFile)
	decoder.Decode(config)

	fmt.Printf("Current MaxFiles is: %d", config.App.Log.MaxFiles)

	config.App.Log.MaxFiles = 4
	writeFile, err := os.OpenFile("main.toml.new", os.O_CREATE|os.O_RDWR, 0644)
	checkErr(err)
	defer writeFile.Close()

	encoder := toml.NewEncoder(writeFile)
	encoder.Encode(config)
}

一般来说我们可以使用config struct或者一个map[string]interface{}来接收从toml配置文件或toml string中的配置,但是两者都有一些缺陷:使用config struct需要预先定义好包含详细列信息的struct(使用interface{}代替亦可不过那和直接使用map没什么区别了),使用map[string]interface{}那么在获取配置值时需要大量的使用类型断言。

那么有没有一种类似python语言中那种直接得到一个dict类型然后直接获得配置值的方式?

go-toml包提供了Tree类型来实现这种需求,他有些类似于使用map[string]interface{}但是不再需要那么多层的断言了,而且包含了很多便捷的method,直接示例:

package main

import (
	"fmt"
	"log"
	"os"
	toml "github.com/pelletier/go-toml"
)

func checkErr(err error){
	if err != nil{
		log.Fatalln(err)
	}
}

func main() {
	configTree,err := toml.LoadFile("main.toml")
	checkErr(err)
	maxFiles := configTree.Get("app.log.max_files").(int64) //坑1:数字被默认解析为int64类型,所以必须断言为int64
	fmt.Printf("Current MaxFiles is: %d", maxFiles)

	writeFile,err := os.OpenFile("main.toml.new", os.O_CREATE|os.O_RDWR, 0644)
	checkErr(err)
	defer writeFile.Close()

	var newMaxFiles int64 = 4 //坑2:定义为int64,原因同上
	configTree.Set("app.log.max_files", newMaxFiles)
	_,err = configTree.WriteTo(writeFile) //小改进:写入的toml文件中所有key都和原来一样是小写的,不用再担心大小写的问题,使用map处理时是否会有大小写问题懒的测啦
	checkErr(err)
}

  

标签:struct,err,Golang,yaml,toml,config,string
来源: https://www.cnblogs.com/leohahah/p/14863128.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有