zoukankan      html  css  js  c++  java
  • Golang yaml与toml解析

    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:
    %v
    
    ", config)
    
    	//marshal config to bytes
    	configBytes, err := yaml.Marshal(config)
    	if err != nil {
    		log.Fatalf("error: %v", err)
    	}
    	fmt.Printf("--- config dumps:
    %s
    
    ", 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:
    %v
    
    ", m)
    
    	//marshal map to bytes
    	d, err := yaml.Marshal(&m)
    	if err != nil {
    		log.Fatalf("error: %v", err)
    	}
    	fmt.Printf("--- map dumps:
    %s
    
    ", 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)
    }
    

      

    想建一个数据库技术和编程技术的交流群,用于磨炼提升技术能力,目前主要专注于Golang和Python以及TiDB,MySQL数据库,群号:231338927,建群日期:2019.04.26,截止2021.02.01人数:300人 ... 如发现博客错误,可直接留言指正,感谢。
  • 相关阅读:
    Java中last_insert_id的使用
    Java上传视频
    Java创建Excel-DEMO
    导出excel表格
    Java导入excel并保存到数据库
    Java基础13一异常
    Java基础12一IO流
    腾讯云-等保要求
    云安全等保项目服务内容及云安全产品清单-(腾讯云部分)
    《网络风险及网络安全》培训总结
  • 原文地址:https://www.cnblogs.com/leohahah/p/14863128.html
Copyright © 2011-2022 走看看