Reputation: 934
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
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
}
Upvotes: 0