parsing - 将 yaml 字段动态解析为 Go 中的一组有限结构之一

标签 parsing go yaml marshalling unmarshalling

我有一个 yaml 文件,其中一个字段可以用一种可能的结构来表示。为了简化代码和 yaml 文件,假设我有这些 yaml 文件:

kind: "foo"
spec:
  fooVal: 4
kind: "bar"
spec:
  barVal: 5
这些用于解析的结构:
    type Spec struct {
        Kind string      `yaml:"kind"`
        Spec interface{} `yaml:"spec"`
    }
    type Foo struct {
        FooVal int `yaml:"fooVal"`
    }
    type Bar struct {
        BarVal int `yaml:"barVal"`
    }
我知道我可以使用 map[string]interface{} 作为 Spec 字段的一种类型。但真实的例子更复杂,涉及更多可能的结构体类型,不仅仅是 FooBar ,这就是为什么我不喜欢将 spec 解析到字段中。
我找到了一种解决方法:将 yaml 解码为中间结构,然后检查 kind 字段,并将 map[string]interface{} 字段编码到 yaml 中,并将其解码为具体类型:
    var spec Spec
    if err := yaml.Unmarshal([]byte(src), &spec); err != nil {
        panic(err)
    }
    tmp, _ := yaml.Marshal(spec.Spec)
    if spec.Kind == "foo" {
        var foo Foo
        yaml.Unmarshal(tmp, &foo)
        fmt.Printf("foo value is %d\n", foo.FooVal)
    }
    if spec.Kind == "bar" {
        tmp, _ := yaml.Marshal(spec.Spec)
        var bar Bar
        yaml.Unmarshal(tmp, &bar)
        fmt.Printf("bar value is %d\n", bar.BarVal)
    }
但它需要额外的步骤并消耗更多的内存(真实的 yaml 文件可能比示例中的更大)。是否存在一些更优雅的方法来将 yaml 动态解码为一组有限的结构?
更新:我正在使用 github.com/go-yaml/yaml v2.1.0 Yaml 解析器。

最佳答案

要与 yaml.v2 一起使用,您可以执行以下操作:

type yamlNode struct {
    unmarshal func(interface{}) error
}

func (n *yamlNode) UnmarshalYAML(unmarshal func(interface{}) error) error {
    n.unmarshal = unmarshal
    return nil
}

type Spec struct {
    Kind string      `yaml:"kind"`
    Spec interface{} `yaml:"-"`
}
func (s *Spec) UnmarshalYAML(unmarshal func(interface{}) error) error {
    type S Spec
    type T struct {
        S    `yaml:",inline"`
        Spec yamlNode `yaml:"spec"`
    }

    obj := &T{}
    if err := unmarshal(obj); err != nil {
        return err
    }
    *s = Spec(obj.S)

    switch s.Kind {
    case "foo":
        s.Spec = new(Foo)
    case "bar":
        s.Spec = new(Bar)
    default:
        panic("kind unknown")
    }
    return obj.Spec.unmarshal(s.Spec)
}
https://play.golang.org/p/Ov0cOaedb-x

要与 yaml.v3 一起使用,您可以执行以下操作:
type Spec struct {
    Kind string      `yaml:"kind"`
    Spec interface{} `yaml:"-"`
}
func (s *Spec) UnmarshalYAML(n *yaml.Node) error {
    type S Spec
    type T struct {
        *S   `yaml:",inline"`
        Spec yaml.Node `yaml:"spec"`
    }

    obj := &T{S: (*S)(s)}
    if err := n.Decode(obj); err != nil {
        return err
    }

    switch s.Kind {
    case "foo":
        s.Spec = new(Foo)
    case "bar":
        s.Spec = new(Bar)
    default:
        panic("kind unknown")
    }
    return obj.Spec.Decode(s.Spec)
}
https://play.golang.org/p/ryEuHyU-M2Z

关于parsing - 将 yaml 字段动态解析为 Go 中的一组有限结构之一,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66709979/

相关文章:

c# - 为什么 xls 在从代码读取时/之后打开?

go - 我正在尝试将 Cobra 集成到我的程序中

loops - 如何在不运行以下脚本的情况下退出循环

json - yml 文件中不允许重复标签

java - 如何修复超出 START_ARRAY token 错误

c# - 如何在 dotnet 中将 ""解析为 long ("0")

java - 解析字符串

c - 进行 URL 匹配和标签提取的有效方法是什么?

syntax - 在yaml中写#(在字符串中)

yaml - CloudFormation yaml - 如何强制数字类型?