Nox
Nox

Reputation: 934

YAML parsing unmarshal fails

I have read a lot of posts, but still can't fix it after multiple hours of research and tries, here is my problem.

I have this YAML file:

---
filename: "Filename.txt"
steps:
  -
    mutationRaw:
      name: "Random things..."
      pattern: "12 34 56 78 9a bc de f0"
      mask: "xxxxxxxx"
      offset: 0
      replace: "f0 de bc 9a 78 56 34 12"
  -
    mutationRaw:
      name: "Some other random things..."
      pattern: "00 00 12 34 56 78 9a bc de f0"
      mask: "xxxxxxxxxx"
      offset: 2
      replace: "11 11 f0 de bc 9a 78 56 34 12"

Here is my Go code:

package main

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

type patcherT struct {
    Filename string `yaml:"filename"`
    Steps    struct {
        MutationsRaw []struct {
            Name      string `yaml:"name"`
        } `yaml:"mutationRaw,omitempty"`
    } `yaml:"steps"`
}

func main() {
    data, err := ioutil.ReadFile("struct.yaml")
    if err != nil {
        panic(err)
    }

    var patcher patcherT
    err = yaml.Unmarshal(data, &patcher)
    if err != nil {
        panic(err)
    }

    fmt.Println("Patcher for ", patcher.Filename)
    fmt.Println("Steps :")
    for i := 0; i < len(patcher.Steps.MutationsRaw); i++ {
        fmt.Println("\t- ", patcher.Steps.MutationsRaw[i].Name)
    }
}

And this error:

line 4: cannot unmarshal !!seq into struct { MutationsRaw []struct { Name string "yaml:"name"" } "yaml:"mutationRaw,omitempty"" }

Note that it is possible to have, instead of mutationRaw, mutationFile, deletionRaw, deletionFile, and some others. I could just do this:

type patcherT struct {
    Filename string `yaml:"filename"`
    Steps    []struct {
        MutationsRaw struct {
            Name      string `yaml:"name"`
        } `yaml:"mutationRaw,omitempty"`
    } `yaml:"steps"`
}

However, I would lose too much information if I keep the same YAML structure. I would really like to be able to parse my patcherT structure through patcher.Steps.MutationRaw[i], patcher.Steps.MutationFile[i], ... The first structure makes this parsing possible, but does not work, while the second makes this parsing harder and requires YAML changes, but works.

In brief, how can I parse such a YAML structure to allow me to iterate not on Steps, but on MutationsRaw (or MutationsFile, ...)?

Upvotes: 0

Views: 1141

Answers (1)

kozmo
kozmo

Reputation: 4490

You can implement yaml.Unmarshaler interface with custom unmarshal logic in UnmarshalYAML method.

For Example:

type patcherT struct {
    Filename string `yaml:"filename"`
    Steps   Steps `yaml:"steps"`
}

type Steps struct {
    MutationsRaw []struct {
        Name string `yaml:"name"`
    } `yaml:"mutationRaw,omitempty"`
}

func (s *Steps) UnmarshalYAML(unmarshal func(interface{}) error) error {
    var tmpSteps []struct {
        TmpMutationsRaw struct {
            Name string `yaml:"name"`
        } `yaml:"mutationRaw,omitempty"`
    }
    err := unmarshal(&tmpSteps)
    if err != nil {
        return err
    }
    *s = Steps{MutationsRaw: make([]struct {
        Name string `yaml:"name"`
    }, len(tmpSteps))}
    for i, val := range tmpSteps {
        s.MutationsRaw[i] = struct {
            Name string `yaml:"name"`
        }(struct{ Name string }{Name: val.TmpMutationsRaw.Name})
    }
    return nil
}

PLAYGROUND

Upvotes: 0

Related Questions