Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom UnmarshalYAML, how to implement Unmarshaler interface on custom type

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?

like image 450
skaldesh Avatar asked Mar 28 '18 09:03

skaldesh


1 Answers

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
}
like image 90
skaldesh Avatar answered Oct 15 '22 22:10

skaldesh