I write a recursive function that iterate over deep nested struct like the following:
type Container struct {
Name string
Items []Item
}
type Item struct {
Name string
Info Info
Vals []string
}
// recursively reads nested struct, prints string values
func ReadStruct(st interface{}) {
val := reflect.ValueOf(st).Elem()
for i := 0; i < val.NumField(); i++ {
fmt.Println(val.Type().Field(i).Type.Kind())
switch val.Type().Field(i).Type.Kind() {
case reflect.Struct:
ReadStruct(val.Field(i)) // panic: call of reflect.Value.Elem on struct Value
case reflect.Slice:
// How to iterate over the reflect.Slice?
case reflect.String:
fmt.Printf("%v=%v", val.Type().Field(i).Name, val.Field(i))
}
}
how to get access to inner objects (slices, structs) to work with them using reflect? to iterate over slice i tried to use:
for i:= 0; i < val.Field(i).Slice(0, val.Field(i).Len()); i++ { //error: reflect.Value doesnt support indexing
//some work
}
Couple of errors in your code.
First, you only have to call Value.Elem()
if the passed value is a pointer. When you iterate over the fields and you find a field of struct type, and you recursively call ReadStruct()
with that, that won't be a pointer and thus you mustn't call Elem()
on that.
So do it like this:
val := reflect.ValueOf(st)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
Next, since you start ReadStruct()
by calling reflect.ValueOf()
, that assumes you have to pass non-reflect values to ReadStruct()
(that is, not values of type reflect.Value
).
But when you iterate over the fields, calling Value.Field()
, you get a reflect.Value
wrapping the field. You have to call Value.Interface()
to extract the non-reflect value form it, to be passed in recursive calls.
To iterate over slices, simply use Value.Len()
to get the slice length, and Value.Index()
to get the ith element of the slice.
Here's the corrected version of your traversal function:
// I used this type as you didn't post it in your question.
type Info struct {
Key, Value string
}
func ReadStruct(st interface{}) {
val := reflect.ValueOf(st)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
for i := 0; i < val.NumField(); i++ {
// fmt.Println(val.Type().Field(i).Type.Kind())
f := val.Field(i)
switch f.Kind() {
case reflect.Struct:
ReadStruct(f.Interface())
case reflect.Slice:
for j := 0; j < f.Len(); j++ {
ReadStruct(f.Index(i).Interface())
}
case reflect.String:
fmt.Printf("%v=%v\n", val.Type().Field(i).Name, val.Field(i).Interface())
}
}
}
Testing it:
c := &Container{
Name: "c1",
Items: []Item{
{
Name: "i1",
Info: Info{Key: "k1", Value: "v1"},
},
{
Name: "i2",
Info: Info{Key: "k2", Value: "v2"},
},
},
}
ReadStruct(c)
Output (try it on the Go Playground):
Name=c1
Name=i2
Key=k2
Value=v2
Name=i2
Key=k2
Value=v2
Note: By using recursive calls, you are extracting and re-acquiring reflect.Value
values. It would be more efficient to always work with reflect.Value
s, so you can avoid these unnecessary calls.
This is how you could do that:
func ReadStruct(st interface{}) {
readStruct(reflect.ValueOf(st))
}
func readStruct(val reflect.Value) {
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
for i := 0; i < val.NumField(); i++ {
// fmt.Println(val.Type().Field(i).Type.Kind())
f := val.Field(i)
switch f.Kind() {
case reflect.Struct:
readStruct(f)
case reflect.Slice:
for j := 0; j < f.Len(); j++ {
readStruct(f.Index(i))
}
case reflect.String:
fmt.Printf("%v=%v\n", val.Type().Field(i).Name, val.Field(i))
}
}
}
This will output the same. Try this one on the Go Playground.