Go中如何将YAML对象动态解析为对应结构体类型
解决方案:用yaml.v3自定义解组实现接口类型的动态解析
1. 定义通用Rule接口
先搞一个Rule接口,加个空方法IsRule()当标记,所有规则结构体都实现这个方法,这样Go就认它们是Rule类型了。
2. 让规则结构体实现Rule接口
给TypeA和TypeB各加个空的IsRule()方法就行,不用写逻辑,就是凑接口要求。
3. 给Config写自定义解组逻辑
yaml默认不会把节点解析成接口,所以得自己写解析逻辑:
- 先整个临时结构体,把
rules下面的原始yaml节点列表读出来 - 挨个遍历每个规则节点,转成map看里面的键是
typeA还是typeB - 根据键创建对应的结构体实例,把节点内容解析进去,再塞到
Config.Rules里
4. 用类型断言区分规则类型
解析完之后,遍历Config.Rules时,用类型断言判断是TypeA还是TypeB,然后处理对应的业务逻辑。
优势
- 不用搞那个虚拟的
Rule结构体了,直接用接口存规则 - 以后加新规则类型,只需要加个case分支和实现
IsRule()方法就行 - 不用再写一堆判断字段是否为nil的冗余代码,逻辑更清爽
完整代码实现:
package main import ( "fmt" "log" "gopkg.in/yaml.v3" ) // Rule 通用规则接口 type Rule interface { IsRule() // 标记方法,用于标识规则类型 } type TypeA struct { Tic int `yaml:"tic"` Plop int `yaml:"plop"` } // 实现Rule接口 func (TypeA) IsRule() {} type TypeB struct { Tic int `yaml:"tic"` Tac int `yaml:"tac"` Plip int `yaml:"plip"` } // 实现Rule接口 func (TypeB) IsRule() {} type Config struct { Rules []Rule `yaml:"rules"` } // 为Config实现自定义YAML解组逻辑 func (c *Config) UnmarshalYAML(node *yaml.Node) error { // 临时结构体用于读取原始规则节点 type tempConfig struct { Rules []yaml.Node `yaml:"rules"` } var temp tempConfig if err := node.Decode(&temp); err != nil { return err } c.Rules = make([]Rule, len(temp.Rules)) for i, ruleNode := range temp.Rules { // 将规则节点解码为map,判断类型键 var ruleMap map[string]yaml.Node if err := ruleNode.Decode(&ruleMap); err != nil { return err } for key := range ruleMap { switch key { case "typeA": var ta TypeA if err := ruleMap[key].Decode(&ta); err != nil { return err } c.Rules[i] = ta case "typeB": var tb TypeB if err := ruleMap[key].Decode(&tb); err != nil { return err } c.Rules[i] = tb default: return fmt.Errorf("未知规则类型: %s", key) } // 每个规则只有一个类型键,处理完就跳出 break } } return nil } func main() { var yamlFile = []byte(` rules: - typeA: tic: 1 plop: 4 - typeB: tic: 1 tac: 3 plip: 4 - typeA: tic: 2 plop: 5 `) var config Config err := yaml.Unmarshal(yamlFile, &config) if err != nil { log.Fatalf("解析YAML失败: %v", err) } // 遍历并处理不同类型的规则 for _, rule := range config.Rules { switch r := rule.(type) { case TypeA: fmt.Printf("TypeA - Tic: %d, Plop: %d\n", r.Tic, r.Plop) case TypeB: fmt.Printf("TypeB - Tic: %d, Tac: %d, Plip: %d\n", r.Tic, r.Tac, r.Plip) } } }
内容的提问来源于stack exchange,提问作者pge
相关产品推荐
相关产品推荐

