Go YAML反序列化如何仅通过结构体标签直接访问嵌套内层键值
答案
仅依靠gopkg.in/yaml.v2(含v3版本)默认的结构体标签,无法实现你要的点分隔路径跨层级绑定效果。
yaml解析库处理结构体标签时,存在两个固定默认行为:
- 只会把标签值作为当前结构层级对应的直接键名,不会自动解析
.作为路径分隔符做层级穿透 - 不会主动遍历map类型的动态key匹配深层字段
你写的yaml:"companies.google.ceo"标签,会让解析器去YAML根节点查找字面量名为companies.google.ceo的键,完全不会进入companies节点下找google子节点的ceo属性,所以运行示例时GoogleCEO字段会始终为空字符串。
你可以选以下两种常用方案实现需求:
方案1:反序列化后手动赋值(最轻量)
不需要改结构体定义,正常完成反序列化后,直接从已经解析好的Companies map里取出对应值赋值即可,适合只需要取少量固定深层字段的场景:
func main() { var config Config yamlFile := []byte(`companies: google: ceo: "Sundar Pichai" amazon: ceo: "Jeff Bezos"`) err := yaml.Unmarshal(yamlFile, &config) check(err) // 补充一行赋值逻辑即可 if google, exist := config.Companies["google"]; exist { config.GoogleCEO = google.Ceo } fmt.Printf("config:\n%#v", config) }
方案2:实现自定义反序列化方法
如果想把字段填充逻辑封装到解析流程里,不需要每次反序列化后手动写赋值逻辑,可以给Config实现yaml库提供的Unmarshaler接口,反序列化时会自动执行自定义逻辑填充字段:
func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error { // 定义中间结构体避免递归触发自定义反序列化 type rawConfig struct { Companies map[string]Org `yaml:"companies"` } var raw rawConfig if err := unmarshal(&raw); err != nil { return err } c.Companies = raw.Companies // 自动填充GoogleCEO字段 if google, exist := raw.Companies["google"]; exist { c.GoogleCEO = google.Ceo } return nil }
添加该方法后,原main函数不需要做任何修改,反序列化完成后GoogleCEO就会被自动赋值。
补充说明:点分隔路径的深层配置绑定,是viper这类上层配置封装库提供的能力,并非底层yaml反序列化库的默认特性,不能直接通过写点分隔的结构体标签实现。
内容的提问来源于stack exchange,提问作者FeedFlameLove
相关产品推荐
相关产品推荐

