Search code examples
goyamlunmarshalling

Unmarshal yaml while keeping extra parameters


I've been trying to write a custom UnmarshalYAML function that will unmarshal a yaml to a struct and also keep unexpected parameters saving them into a map into the struct itself.

I've tried writing this (https://go.dev/play/p/azf2hksriQ1) but, of course, I get an infinite loop when I try to unmarshal the struct inside the UnmarshalYAML.

Does anyone have any suggestion on how to make this work? Thank you very much!


Solution

  • You can use a simple wrapping technique where you define a type alias for your original type (so yaml.Unmarshaler won't call your UmarshalYAML method) and create a struct which embeds all known and unknown parameters.

    type Foo struct {
        Param1     string         `yaml:"param1"`
        Param2     []int          `yaml:"param2"`
        // NOTE: we explicitly ignore this parameter so it won't be 
        // eventually unmarshaled 
        Extensions map[string]any `yaml:"-"` 
    }
    
    // UnmarshalYAML implements the yaml.UnmarshalYAML interface.
    func (f *Foo) UnmarshalYAML(value *yaml.Node) error {
        // Here we are wrapping Foo, so it will be unmarshaled
        // using without calling foo.UnmarshalYAML
        type Wrapped Foo
        // NOTE: for yaml it's required to provide inline
        // option if we would like to embed
        var val struct {
            Wrapped    `yaml:",inline"`
            Extensions map[string]any `yaml:",inline"`
        }
        if err := value.Decode(&val); err != nil {
            return err
        }
        *f = Foo(val.Wrapped)
        f.Extensions = val.Extensions
        return nil
    }
    

    Playground Link