亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

將 yaml 字段動態解析為 Go 中的有限結構集之一

將 yaml 字段動態解析為 Go 中的有限結構集之一

Go
滄海一幻覺 2022-08-15 19:39:23
我有一個文件,其中一個字段可以由一種可能的結構表示。為了簡化代碼和 yaml 文件,假設我有這些 yaml 文件:yamlkind: "foo"spec:  fooVal: 4kind: "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{}SpecFooBarspec我找到了一個解決方法:將yaml解構到中間結構中,然后檢查字段,將字段封送到yaml后面,然后將其取消到具體類型:kindmap[string]interface{}    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動態地解構為一組有限的結構?更新:我正在使用 Yaml 解析器。github.com/go-yaml/yaml v2.1.0
查看完整描述

2 回答

?
開心每一天1111

TA貢獻1836條經驗 獲得超13個贊

您可以通過實現自定義函數來執行此操作。但是,對于API的版本,您基本上可以像現在一樣做同樣的事情,并且只是更好地封裝它。UnmarshalYAMLv2


但是,如果您切換到使用API,則可以獲得更好的效果,實際上允許您在將解析的YAML節點處理為本機Go類型之前對其進行處理。這是它的外觀:v3UnmarshalYAML


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)

}

我建議研究使用YAML標簽而不是當前結構的可能性,以便在YAML中對此進行建模;標記正是為此目的而設計的。而不是當前的 YAML


kind: "foo"

spec:

  fooVal: 4

你可以寫


--- !foo

fooVal: 4

現在,您不再需要描述結構了。加載此內容看起來會有所不同,因為您需要一個可以定義的包裝根類型,但如果這只是較大結構的一部分,則可能是可行的。您可以在 的字段中訪問該標簽。kindspecUnmarshalYAML!fooyaml.NodeTag


查看完整回答
反對 回復 2022-08-15
?
森林海

TA貢獻2011條經驗 獲得超2個贊

要與一起使用,您可以執行以下操作: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


查看完整回答
反對 回復 2022-08-15
  • 2 回答
  • 0 關注
  • 203 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號