Custom UnmarshalYAML, how to implement Unmarshaler interface on custom type
Asked Answered
U

1

18

I parse a .yaml file and need to unmarshal one of its properties in a custom manner. I am using the "gopkg.in/yaml.v2" package.

The property in question is stored like this in my .yaml file:

endPointNumberSequences:
  AD1: [ 0, 10, 14, 1, 11, 2, 100, 101, 12 ]

So it is basically a map[string][]uint16 kind of type.
But I need map[string]EpnSeq where EpnSeq is defined as:
type EpnSeq map[uint16]uint16

My struct:

type CitConfig struct {
    // lots of other properties
    // ...

    EndPointNumberSequences  map[string]EpnSeq `yaml:"endPointNumberSequences"`
}

I tried to implement the Unmarshaler interface on it like this:

// Implements the Unmarshaler interface of the yaml pkg.
func (e EpnSeq) UnmarshalYAML(unmarshal func(interface{}) error) error {
    yamlEpnSequence := make([]uint16, 0)
    err := unmarshal(&yamlEpnSequence)
    if err != nil {
        return err
    }

    for priority, epn := range yamlEpnSequence {
        e[epn] = uint16(priority) // crashes with nil pointer
    }

    return nil
}

My problem is that inside the UnmarshalYAML function the EpnSeq type is not defined, causing a nil pointer exception at runtime.
How do I correctly implement the Unmarshaler interface here?

Underrate answered 28/3, 2018 at 9:8 Comment(3)
make EpnSeq before writing to it? E.g. *e = make(EpnSeq, len(yamlEpnSequence)). Needs a pointer receiver anyway.Microvolt
wow, I was just being silly. I tried this, but I failed to dereference the pointer first, before assigning with make(). That way, the pointer was only changed locally... my bad, sorryUnderrate
@Volker: Write an answer and get yourself some internet points, woohoo ;)Underrate
U
16

Since @Volker did not post his comment as answer, I will do for the sake of completeness.
So I was already on the right path, but simply failed to dereference the pointer receiver of my struct, when initializing it:

// Implements the Unmarshaler interface of the yaml pkg.
func (e *EpnSeq) UnmarshalYAML(unmarshal func(interface{}) error) error {
    yamlEpnSequence := make([]uint16, 0)
    err := unmarshal(&yamlEpnSequence)
    if err != nil {
        return err
    }

    // make sure to dereference before assignment, 
    // otherwise only the local variable will be overwritten
    // and not the value the pointer actually points to
    *e = make(EpnSeq, len(yamlEpnSequence))
    for priority, epn := range yamlEpnSequence {
        e[epn] = uint16(priority) // no crash anymore
    }

    return nil
}
Underrate answered 7/5, 2018 at 9:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.