问题
I am trying to reflect recursively over a struct, printing out the type of each field. Where a field is an slice of structs, I'd like to be able to identify the type held in the array and then reflect over that type.
Here is some sample code
package main
import (
"log"
"reflect"
)
type child struct {
Name *string
Age int
}
type Parent struct {
Name string
Surname *string
Children []*child
PetNames []string
}
func main() {
typ := reflect.TypeOf(Parent{})
log.Printf("This is a : %s", typ.Kind())
for i := 0; i < typ.NumField(); i++ {
p := typ.Field(i)
if !p.Anonymous {
switch p.Type.Kind() {
case reflect.Ptr:
log.Printf("Ptr: %s is a type %s", p.Name, p.Type)
case reflect.Slice:
log.Printf("Slice: %s is a type %s", p.Name, p.Type)
subtyp := p.Type.Elem()
if subtyp.Kind() == reflect.Ptr {
subtyp = subtyp.Elem()
}
log.Printf("\tDereferenced Type%s", subtyp)
default:
log.Printf("Default: %s is a type %s", p.Name, p.Type)
}
}
}
}
The output looks like this:
This is a : struct
Default: Name is a type string
Ptr: Surname is a type *string
Slice: Children is a type []*main.child
Dereferenced Type main.child
Slice: PetNames is a type []string
Dereferenced Type string
When I identify that a field type is a slice of pointers, I am able to infer the type by calling subtype.Elem().
The output is 'main.child'
If I then try to reflect child using
subSubType := reflect.TypeOf(subtyp)
log.Printf("%+v", subSubType)
I get the following:
*reflect.rtype
How can I use the reflection API to iterate over the fields of the child struct?
回答1:
Here's one way to do it.
func printType(prefix string, t reflect.Type, visited map[reflect.Type]bool) {
// Print the name of this type with opening ( for description.
fmt.Printf("%s (", t)
// Traverse elements, adding to description as we go.
elems:
for {
switch t.Kind() {
case reflect.Ptr:
fmt.Print("ptr to ")
case reflect.Slice:
fmt.Print("slice of ")
case reflect.Array:
fmt.Printf("array with %d elements of ", t.Len())
default:
break elems
}
t = t.Elem()
}
// Print the kind of the type and the closing ) of the description.
// In the case of a struct, we print the names of the fields and recurse.
switch t.Kind() {
case reflect.Struct:
fmt.Printf("struct with %d fields)\n", t.NumField())
if visited[t] {
// Don't blow up on recursive type definition.
break
}
visited[t] = true
prefix += " "
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
fmt.Print(prefix, f.Name, " ")
printType(prefix, f.Type, visited)
}
default:
fmt.Printf("%s)\n", t.Kind())
}
}
func main() {
printType("", reflect.TypeOf(Parent{}), make(map[reflect.Type]bool))
}
The output for Parent{} given the following types:
type child struct {
Name *string
Age int
}
type Parent struct {
Name string
Surname *string
Children []*child
PetNames []string
Parents [2]*Parent
child
}
is:
main.Parent (struct with 6 fields)
Name string (string)
Surname *string (ptr to string)
Children []*main.child (slice of ptr to struct with 2 fields)
Name *string (ptr to string)
Age int (int)
PetNames []string (slice of string)
Parents [2]*main.Parent (array with 2 elements of ptr to struct with 6 fields)
child main.child (struct with 2 fields)
playground example
来源:https://stackoverflow.com/questions/47624318/golang-recurisive-reflection