How to reflect struct recursive in golang
Asked Answered
H

1

5

I want to reflect the struct type and value recursively, but it fails. I don't know how to pass the sub struct recursively.

It error the following.

panic: reflect: NumField of non-struct type

goroutine 1 [running]:
reflect.(*rtype).NumField(0xc0b20, 0xc82000a360)
    /usr/local/go/src/reflect/type.go:660 +0x7b

I have two struct Person and Name

type Person struct {
    Fullname NameType
    Sex      string
}

type Name struct {
    Firstname string
    Lastname  string
}

I define the Person in main, and display the struct with recursive function.

person := Person{
    Name{"James", "Bound"},
    "Male",
}

display(&person)

The display function recursive display the struct.

func display(s interface{}) {
    reflectType := reflect.TypeOf(s).Elem()
    reflectValue := reflect.ValueOf(s).Elem()

    for i := 0; i < reflectType.NumField(); i++ {
        typeName := reflectType.Field(i).Name

        valueType := reflectValue.Field(i).Type()
        valueValue := reflectValue.Field(i).Interface()

        switch reflectValue.Field(i).Kind() {
        case reflect.String:
            fmt.Printf("%s : %s(%s)\n", typeName, valueValue, valueType)
        case reflect.Int32:
            fmt.Printf("%s : %i(%s)\n", typeName, valueValue, valueType)
        case reflect.Struct:
            fmt.Printf("%s : it is %s\n", typeName, valueType)
            display(&valueValue)
        }

    }
}
Handcart answered 28/12, 2015 at 11:54 Comment(2)
Check this link for soluton: #25047924Catenate
If don't care to do this on your own, or want to see how others do it, check out this really good pretty printer for Go github.com/davecgh/go-spewYouth
H
7

Inside your display function, you declare valueValue as:

valueValue := reflectValue.Field(i).Interface()

So valueValue is of type interface{}. Inside the for loop, you have a recursive call to display:

display(&valueValue)

So it is being called with an argument of type *interface{}. Inside the recursive call, reflectType will represent interface{} rather than the type that happens to be stored within the value. Since NumField can only be called on reflect.Type's representing structs, you get a panic.

If you want to call display with a pointer to the struct instead, you could do so with something like this:

v := valueValue := reflectValue.Field(i).Addr()
display(v.Interface())
Hovey answered 28/12, 2015 at 13:12 Comment(2)
@It is perfect right. I am very confused by the reflect, there are reflect.TypeOf, reflect.ValueOf and so many methods. I need more time to investigate them. :)Handcart
is it possible to modify struct value inside "display" function? @James HenstridgeAvantgarde

© 2022 - 2024 — McMap. All rights reserved.