Unmarshalling YAML to ordered maps

Issue

I am trying to unmarshal the following YAML with Go YAML v3.

model:
  name: mymodel
  default-children:
  - payment

  pipeline:
    accumulator_v1:
      by-type:
        type: static
        value: false
      result-type:
        type: static
        value: 3

    item_v1:
      amount:
        type: schema-path
        value: amount
      start-date:
        type: schema-path
        value: start-date

Under pipeline is an arbitrary number of ordered items. The struct to which this should be unmarshalled looks like this:

type PipelineItemOption struct {
        Type string
        Value interface{}
}

type PipelineItem struct {
        Options map[string]PipelineItemOption
}

type Model struct {
        Name string
        DefaultChildren []string `yaml:"default-children"`
        Pipeline orderedmap[string]PipelineItem    // "pseudo code"
}

How does this work with Golang YAML v3? In v2 there was MapSlice, but that is gone in v3.

Solution

You claim that marshaling to an intermediate yaml.Node is highly non-generic, but I don’t really see why. It looks like this:

package main

import (
    "fmt"
    "gopkg.in/yaml.v3"
)

type PipelineItemOption struct {
        Type string
        Value interface{}
}

type PipelineItem struct {
    Name string
        Options map[string]PipelineItemOption
}

type Pipeline []PipelineItem

type Model struct {
        Name string
        DefaultChildren []string `yaml:"default-children"`
        Pipeline Pipeline
}

func (p *Pipeline) UnmarshalYAML(value *yaml.Node) error {
    if value.Kind != yaml.MappingNode {
        return fmt.Errorf("pipeline must contain YAML mapping, has %v", value.Kind)
    }
    *p = make([]PipelineItem, len(value.Content)/2)
    for i := 0; i < len(value.Content); i += 2 {
        var res = &(*p)[i/2]
        if err := value.Content[i].Decode(&res.Name); err != nil {
            return err
        }
        if err := value.Content[i+1].Decode(&res.Options); err != nil {
            return err
        }
    }
    return nil
}


var input []byte = []byte(`
model:
  name: mymodel
  default-children:
  - payment

  pipeline:
    accumulator_v1:
      by-type:
        type: static
        value: false
      result-type:
        type: static
        value: 3

    item_v1:
      amount:
        type: schema-path
        value: amount
      start-date:
        type: schema-path
        value: start-date`)

func main() {
    var f struct {
        Model Model
    }
    var err error
    if err = yaml.Unmarshal(input, &f); err != nil {
        panic(err)
    }
    fmt.Printf("%v", f)
}

Answered By – flyx

Answer Checked By – Pedro (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.