golang 使用 viper 加载配置文件 自动反序列化到结构

文章博客地址:golang 使用 viper 加载配置 自动反序列化到结构

  • golang使用 viper 无需设置 mapstructure tag 根据配置文件后缀 自动返序列化到结构
  • 解决结构有下划线的字段解析不成功问题

viper 正常加载配置文件

golang viper 其中可以用来 查找、加载和反序列化JSON、TOML、YAML、HCL、INI、envfile和格式的配置文件

配置文件 test_toml.toml

ini 复制代码
http_addr = ":8082"
grpc_addr = ":8083"
jaeger_url= "http://localhost:14268/api/traces"
tracing= true

golang代码

golang 复制代码
type ConfigTest struct {
	HttpAddr  string `json:"http_addr" toml:"http_addr" yaml:"http_addr"`
	GrpcAddr  string `json:"grpc_addr" toml:"grpc_addr" yaml:"grpc_addr"`
	JaegerUrl string `json:"jaeger_url" toml:"jaeger_url" yaml:"jaeger_url" mapstructure:"jaeger_url"`
	Tracing   bool   `toml:"tracing"  json:"tracing" yaml:"tracing" ` // opentelemetry tracing
}

// jaeger 加载配置文件
func TestSourceFile_Unmarshal(t *testing.T) {
	filePath := "./test_toml.toml"
	viper.SetConfigFile(filePath)
	if err := viper.ReadInConfig(); err != nil {
		t.Error(err)
	}

	c := &ConfigTest{}
	if err := viper.Unmarshal(c); err != nil {
		t.Error(err)
	}
	logger.Infow("Unmarshal file sucess", "v", c)
}

打印返序列化的配置结构

shell 复制代码
{"level":"info","ts":"2023-08-27T21:35:27.041+0800","caller":"config/source_file_test.go:31","msg":"Unmarshal file sucess","v":{"http_addr":"","grpc_addr":"","jaeger_url":"http://localhost:14268/api/traces","tracing":true}}

可以看到带下划线的字段,不加 mapstructure 标签,是不会反序列化

不加 mapstructure tag实现自动反序列化

查看viper Unmarshal 代码

golang 复制代码
func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
	return decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...))
}
func decode(input interface{}, config *mapstructure.DecoderConfig) error {
	decoder, err := mapstructure.NewDecoder(config)
	if err != nil {
		return err
	}
	return decoder.Decode(input)
}
func NewDecoder(config *DecoderConfig) (*Decoder, error) {
	if config.TagName == "" {
		config.TagName = "mapstructure"
	}
	// ...
}
  • 从代码看出 Viper使用的是 github.com/mitchellh/mapstructure来解析值
  • mapstructure 用于将通用的mapstringinterface{}解码到对应的 Go 结构体中
  • 默认情况下,mapstructure 使用结构体中字段的名称做这个映射,不区分大小写,比如 Name 字段可以映射到 name、NAME、NaMe 等等
  • 如果没有指定 tagName ,则默认为 mapstructure,这也是为什么带下划线的字段不加 mapstructure 标签无法解析的原因
  • viper 中Unmarshal的第二个参数是可以指定 DecoderConfigOption 的,从而可以指定 tagName

viper根据文类型件自动解码到结构

  1. 读取文件后缀比如 toml
  2. 根据后缀设置 tagName
  3. 调用 viper.Unmarshal解析
golang 复制代码
func TestSourceFile_Unmarshal1(t *testing.T) {
	filePath := "./test_toml.toml"
	c := &ConfigTest{}
	if err := viperUnmarshal(c, filePath); err != nil {
		t.Error(err)
	}
	logger.Infow("Unmarshal file sucess", "v", c)
}

func viperUnmarshal(v interface{}, configPath string) error {
	var tagName string
	ext := filepath.Ext(configPath)
	if len(ext) > 1 {
		tagName = ext[1:]
	}
	// set decode tag_name, default is mapstructure
	decoderConfigOption := func(c *mapstructure.DecoderConfig) {
		c.TagName = tagName
	}
	cViper := viper.New()
	cViper.SetConfigFile(configPath)
	if err := cViper.ReadInConfig(); err != nil {
		return err
	}
	return cViper.Unmarshal(v, decoderConfigOption)
}
shell 复制代码
{"level":"info","ts":"2023-08-27T21:35:34.553+0800","caller":"config/source_file_test.go:40","msg":"Unmarshal file sucess","v":{"http_addr":":8082","grpc_addr":":8083","jaeger_url":"http://localhost:14268/api/traces","tracing":true}}

我已将viper加载配置集成进自己的项目,完整example 代码可以查看 source_file_test.go

相关推荐
沙蒿同学2 小时前
不引 Ghostscript:我用纯 Go 给 PDF 做了一场「压缩手术」
前端·后端·github
前进的李工3 小时前
飞书企业知识库设计+后端高频八股
数据库·redis·后端·mysql·性能优化·飞书·rag
金銀銅鐵5 小时前
[Java] 展示增删元素时 LinkedList 的内部变化
java·后端·ai编程
武子康6 小时前
Vercel Cache Reason:相同 MISS,怎样决定下一步查什么
人工智能·后端·agent
杨文说AI与数字化6 小时前
把模型依赖做成可替换的组件:三层适配 + 一套回归评估
后端
IT_陈寒7 小时前
Vue的这个响应式陷阱我居然现在才发现
前端·人工智能·后端
WL学习笔记7 小时前
使用string类
c++·后端·string类
飞鸟真人7 小时前
IM网关系列(九)单账号流量统计:滑动窗口桶方案设计与Go实现
后端·golang
武子康8 小时前
Embedding 替换怎么评测:把索引成本算进成功任务
人工智能·后端·agent
大哥430910 小时前
on-failure 不扛重启:一次 Docker restart 策略的反直觉排查
后端