lundi 23 juillet 2018

How do I use reflect.FieldByName when the value is of kind reflect.Ptr

I have a project function which returns a slice containing the field values by name of each struct or map in an input slice. I am having trouble with case where the input slice contains pointers to structs. I have setup a recursive function to operate on the value, but need to know how to convert from kind reflect.Ptr to the underlying reflect.Struct. How is this done? Any other design recommendations are appreciated. I am still a bit new to Go.

Here is the code:

func project(in []interface{}, property string) []interface{} {

    var result []interface{}

    var appendValue func(list []interface{}, el interface{})

    appendValue = func(list []interface{}, el interface{}) {
        v := reflect.ValueOf(el)
        kind := v.Kind()
        if kind == reflect.Ptr {

            // How do I get the struct behind this ptr?
            // appendValue(list, el)

        } else if kind == reflect.Struct {
            result = append(result, v.FieldByName(property).Interface())
        } else if kind == reflect.Map {
            result = append(result, el.(map[string]interface{})[property])
        } else {
            panic("Value must be a struct or map")
        }
    }

    for _, el := range in {
        appendValue(result, el)
    }

    return result

}

... and the test cases:

func Test_project(t *testing.T) {

    cases := map[string]struct {
        input    []interface{}
        property string
        expected []interface{}
    }{
        "simple-map": {
            []interface{}{
                map[string]interface{}{
                    "a": "a1",
                },
            },
            "a",
            []interface{}{"a1"},
        },
        "simple-struct": {
            []interface{}{
                simpleStruct{
                    A: "a1",
                },
            },
            "A",
            []interface{}{"a1"},
        },
        // THIS ONE FAILS
        "simple-struct-ptr": {
            []interface{}{
                &simpleStruct{
                    A: "a1",
                },
            },
            "A",
            []interface{}{"a1"},
        },
    }

    for k, v := range cases {

        t.Run(k, func(t *testing.T) {
            got := project(v.input, v.property)
            if !reflect.DeepEqual(got, v.expected) {
                t.Fatalf("Expected %+v, got %+v", v.expected, got)
            }
        })
    }
}





Aucun commentaire:

Enregistrer un commentaire