使用反射读取嵌套结构

我编写了一个递归函数来迭代深层嵌套结构,如下所示:


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))

        }

    }

  

如何访问内部对象(切片、结构)以使用反射与它们一起工作?遍历我尝试使用的切片:


for i:= 0; i < val.Field(i).Slice(0, val.Field(i).Len()); i++ { //error: reflect.Value doesnt support indexing

//some work


拉莫斯之舞
浏览 84回答 1
1回答

蝴蝶刀刀

您的代码中有几个错误。Value.Elem()首先,只有在传递的值是指针时才需要调用。当您遍历这些字段并找到一个结构类型的字段时,您递归地调用ReadStruct()它,它不会是一个指针,因此您不能调用Elem()它。所以这样做:val&nbsp;:=&nbsp;reflect.ValueOf(st) &nbsp;&nbsp;&nbsp;&nbsp;if&nbsp;val.Kind()&nbsp;==&nbsp;reflect.Ptr&nbsp;{ &nbsp;&nbsp;&nbsp;&nbsp;val&nbsp;=&nbsp;val.Elem() }接下来,由于您首先ReadStruct()调用reflect.ValueOf(),因此假设您必须将非反射值传递给ReadStruct()(即,不是 type 的值reflect.Value)。但是当您遍历这些字段时,调用Value.Field(),您会得到一个reflect.Value包装字段。您必须调用Value.Interface()以从中提取非反射值,以便在递归调用中传递。要迭代切片,只需使用Value.Len()获取切片长度,并Value.Index()获取切片的第 i个元素。这是遍历函数的更正版本:&nbsp; &nbsp; // I used this type as you didn't post it in your question.type Info struct {&nbsp; &nbsp; Key, Value string}func ReadStruct(st interface{}) {&nbsp; &nbsp; val := reflect.ValueOf(st)&nbsp; &nbsp; if val.Kind() == reflect.Ptr {&nbsp; &nbsp; &nbsp; &nbsp; val = val.Elem()&nbsp; &nbsp; }&nbsp; &nbsp; for i := 0; i < val.NumField(); i++ {&nbsp; &nbsp; &nbsp; &nbsp; // fmt.Println(val.Type().Field(i).Type.Kind())&nbsp; &nbsp; &nbsp; &nbsp; f := val.Field(i)&nbsp; &nbsp; &nbsp; &nbsp; switch f.Kind() {&nbsp; &nbsp; &nbsp; &nbsp; case reflect.Struct:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ReadStruct(f.Interface())&nbsp; &nbsp; &nbsp; &nbsp; case reflect.Slice:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for j := 0; j < f.Len(); j++ {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ReadStruct(f.Index(i).Interface())&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; case reflect.String:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("%v=%v\n", val.Type().Field(i).Name, val.Field(i).Interface())&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}测试它:c := &Container{&nbsp; &nbsp; Name: "c1",&nbsp; &nbsp; Items: []Item{&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Name: "i1",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Info: Info{Key: "k1", Value: "v1"},&nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Name: "i2",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Info: Info{Key: "k2", Value: "v2"},&nbsp; &nbsp; &nbsp; &nbsp; },&nbsp; &nbsp; },}ReadStruct(c)输出(在Go Playground上尝试):Name=c1Name=i2Key=k2Value=v2Name=i2Key=k2Value=v2注意:通过使用递归调用,您正在提取和重新获取reflect.Value值。始终使用reflect.Values 会更有效率,因此您可以避免这些不必要的调用。你可以这样做:func ReadStruct(st interface{}) {&nbsp; &nbsp; readStruct(reflect.ValueOf(st))}func readStruct(val reflect.Value) {&nbsp; &nbsp; if val.Kind() == reflect.Ptr {&nbsp; &nbsp; &nbsp; &nbsp; val = val.Elem()&nbsp; &nbsp; }&nbsp; &nbsp; for i := 0; i < val.NumField(); i++ {&nbsp; &nbsp; &nbsp; &nbsp; // fmt.Println(val.Type().Field(i).Type.Kind())&nbsp; &nbsp; &nbsp; &nbsp; f := val.Field(i)&nbsp; &nbsp; &nbsp; &nbsp; switch f.Kind() {&nbsp; &nbsp; &nbsp; &nbsp; case reflect.Struct:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; readStruct(f)&nbsp; &nbsp; &nbsp; &nbsp; case reflect.Slice:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for j := 0; j < f.Len(); j++ {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; readStruct(f.Index(i))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; case reflect.String:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fmt.Printf("%v=%v\n", val.Type().Field(i).Name, val.Field(i))&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}这将输出相同的。在Go Playground试试这个。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go