程序问答   发布时间:2022-06-01  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了将 yaml 字段动态解析为 Go 中的一组有限结构之一大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

如何解决将 yaml 字段动态解析为 Go 中的一组有限结构之一?

开发过程中遇到将 yaml 字段动态解析为 Go 中的一组有限结构之一的问题如何解决?下面主要结合日常开发的经验,给出你关于将 yaml 字段动态解析为 Go 中的一组有限结构之一的解决方法建议,希望对你解决将 yaml 字段动态解析为 Go 中的一组有限结构之一有所启发或帮助;

我有一个 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"`
    }

我知道我可以使用 @H_187_3@map[String]interface{} 作为 Spec 字段的类型。但是真实的例子更复杂,涉及更多可能的结构体类型,不仅仅是Foobar,这也是我不喜欢把spec解析到字段中的原因。>

我找到了一种解决方法:将 yaml 解组为中间结构,然后检查 kind 字段,并将 @H_187_3@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 解析器。

解决方法

您可以通过实现自定义 <my:EventToCommandObserver Grid.column="1" Command="{Binding BindingContext.TextChangedCommand,Mode=OneTime,source={Relativesource FindAncestor,AncestorType={x:Type ItemsView}}}" CommandParameter="{Binding Mode=OneTimE}"> <Entry Text="{Binding value}" HorizontalTextAlignment="Start" HorizontalOptions="FillAndExpand" VerticalOptions="Center" VerticalTextAlignment="Center" Keyboard='Text' ClearButtonVisibility="WhileEdiTing" my:EventToCommandObserver .EventName="TextChanged" /> </my:EventToCommandObserver > 函数来做到这一点。但是,使用 UnmarshalYAML 版本的 API,您基本上可以做与现在相同的事情,只是将其封装得更好一些。

但是,如果您切换到使用 v2 API,您将获得更好的 v3它实际上可以让您在解析的 YAML 节点之前在它被处理为本机去打字。这是它的外观:

UnmarshalYAML

我建议研究使用 YAML 标签而不是您当前的结构在您的 YAML 中对此进行建模的可能性;标签正是为此目的而设计的。而不是当前的 YAML

package main

import (
    "errors"
    "fmt"
    "gopkg.in/yaml.v3"
)

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"`
}

func (s *SpeC) UnmarshalYAML(value *yaml.NodE) error {
    s.Kind = ""
    for i := 0; i < len(value.Content)/2; i += 2 {
        if value.Content[i].Kind == yaml.ScalarNode &&
            value.Content[i].Value == "kind" {
            if value.Content[i+1].Kind != yaml.ScalarNode {
                return errors.New("kind is not a scalar")
            }
            s.Kind = value.Content[i+1].Value
            break
        }
    }
    if s.Kind == "" {
        return errors.New("missing field `kind`")
    }
    switch s.Kind {
    case "foo":
        var foo Foo
        if err := value.Decode(&foo); err != nil {
            return err
        }
        s.Spec = foo
    case "bar":
        var bar Bar
        if err := value.Decode(&bar); err != nil {
            return err
        }
        s.Spec = bar
    default:
        return errors.New("unknown kind: " + s.Kind)
    }
    return nil
}

var input1 = []byte(`
kind: "foo"
spec:
  fooVal: 4
`)

var input2 = []byte(`
kind: "bar"
spec:
  barVal: 5
`)

func main() {
    var s1,s2 Spec
    if err := yaml.Unmarshal(input1,&s1); err != nil {
        panic(err)
    }
    fmt.Printf("Type of spec from input1: %T\n",s1.SpeC)
    if err := yaml.Unmarshal(input2,&s2); err != nil {
        panic(err)
    }
    fmt.Printf("Type of spec from input2: %T\n",s2.SpeC)
}

你可以写

kind: "foo"
spec:
  fooVal: 4

现在您不再需要带有 --- !foo fooVal: 4 kind 的描述结构。加载它看起来有点不同,因为您需要一个可以定义 spec 的包装根类型,但如果这只是更大结构的一部分,它可能是可行的。您可以在 UnmarshalYAML!foo 字段中访问标记 yaml.Node

,

要与 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

大佬总结

以上是大佬教程为你收集整理的将 yaml 字段动态解析为 Go 中的一组有限结构之一全部内容,希望文章能够帮你解决将 yaml 字段动态解析为 Go 中的一组有限结构之一所遇到的程序开发问题。

如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。
标签:yaml