Reputation: 2019
I am trying to iterate over a map of interfaces in golang, it has the below structure, I am able to use for loop to iterate to a single level but couldn't go deep to get values of the interface.
Yaml
steps:
execute:
- mvn : 1.9.3
goals: 'clean install'
concurrent: false
- mvn : 1.9.3
goals: 'dependency-check:check'
concurrent: false
Go
// reading a yaml file
// trying to use "gopkg.in/yaml.v2" package
data, err := ioutil.ReadFile(fileName)
m := make(map[interface{}]interface{})
err = yaml.Unmarshal([]byte(data), &m)
for k, v := range m {
// Trying to explore the data here
fmt.Println("k:", k, "v:", v)
}
Output of
fmt.Printf("--- m:\n%v\n\n", m)
looks like below
map[steps:map[execute:[map[concurrent:false goals:clean install mvn:1.9.3] map[concurrent:false goals:dependency-check:check mvn:1.9.3]]]]
My attempt
for k, v := range m {
fmt.Println("k:", k, "v:", v)
}
Upvotes: 0
Views: 4500
Reputation: 31
Assuming that you have a tree of map[interface{}]interface{} and []interface{}, use the following code to walk the tree:
func walk(v interface{}) {
switch v := v.(type) {
case []interface{}:
for i, v := range v {
fmt.Println("index:", i)
walk(v)
}
case map[interface{}]interface{}:
for k, v := range v {
fmt.Println("key:", k)
walk(v)
}
default:
fmt.Println(v)
}
}
This code recurses down the structure and uses type switches to find the slices and maps.
Use it like this:
data, err := ioutil.ReadFile(fileName)
var m map[interface{}]interface{}
err = yaml.Unmarshal([]byte(data), &m)
walk(m)
Upvotes: 3
Reputation: 306
To access the Execute object and iterate over it you have to do a lot of type assertions like the answer given by @vooker.
Actually it's not recommended to use map[interface{}]interface{}
for unmarshaling process. It should be unmarshalled to a concrete type as a struct.
From the YAML structure, it could be translated as a struct type like this:
type data struct {
Steps struct {
Execute []execute `yaml:"execute"`
} `yaml:"steps"`
}
type execute struct {
MVN string `yaml:"mvn"`
Goals string `yaml:"goals"`
Concurrent bool `yaml:"concurrent"`
}
And from the structure, it is easier to access the unmarshalled data from the YAML. For example, if you want to iterate over the "execute" object you could do something like this:
var result data
yaml.Unmarshal([]byte(str), &result)
fmt.Println(result)
// iterate over the slice of object
for _, e := range result.Steps.Execute {
fmt.Println()
fmt.Println("mvn:", e.MVN)
fmt.Println("goals:", e.Goals)
fmt.Println("concurrent:", e.Concurrent)
}
Try it on the playground
Upvotes: 1
Reputation: 24717
I don't know what you mean with that "value of m" as that doesn't look like any format I've seen in Go so I'll talk to a few situations: When the interfaces are probably types you know what they are vs when the interfaces could be anything and you aren't sure.
If there are only a couple of types that you know went into the map, you can do a type switch and handle each type individually. This would give you the opportunity to reference subfields and print them as well. If you always print the same info for the same type of objects, you could look into adding a String() string
function to your structs which will make them implement the Stringer
interface and then you can print the object and your String() func will get called even if it's boxed as an interface.
If you're working with a library that's filling the map, or there's simply too big a diversity of the types in the map for a type switch to be reasonable, then you'll either want a generic solution such as a library like spew or a custom solution written with reflection.
Upvotes: 1