How to Dynamically Parse a YAML Field to Specific Structs in Go?

Linda Hamilton
Release: 2024-10-28 12:58:30
Original
316 people have browsed it

How to Dynamically Parse a YAML Field to Specific Structs in Go?

Dynamically Parsing a YAML Field to Specific Structs in Go

YAML files often contain fields that can be represented by multiple types of structs. To simplify code and YAML files, consider the following YAML examples:

kind: "foo"
spec:
  fooVal: 4
Copy after login
kind: "bar"
spec:
  barVal: 5
Copy after login

The corresponding structs for parsing are:

<code class="go">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"`
}</code>
Copy after login

While using a map[string]interface{} for the Spec field is an option, it can become complex for larger YAML files.

Elegant Solution Using Custom Unmarshaler

An alternative approach involves creating a custom Unmarshaler for the Spec type. For use with yaml.v2, implement the following:

<code class="go">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:"-"`
}</code>
Copy after login
<code class="go">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)
}</code>
Copy after login

For yaml.v3, use the following:

<code class="go">type Spec struct {
    Kind string      `yaml:"kind"`
    Spec interface{} `yaml:"-"`
}</code>
Copy after login
<code class="go">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)
}</code>
Copy after login

This solution dynamically maps the YAML field to the appropriate struct based on the "kind" field, removing the need for additional steps or memory consumption.

The above is the detailed content of How to Dynamically Parse a YAML Field to Specific Structs in Go?. For more information, please follow other related articles on the PHP Chinese website!

source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Latest Articles by Author
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template
About us Disclaimer Sitemap
php.cn:Public welfare online PHP training,Help PHP learners grow quickly!