You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 04:22:29