为了账号安全,请及时绑定邮箱和手机立即绑定

使用反射读取嵌套结构

使用反射读取嵌套结构

Go
拉莫斯之舞 2023-03-07 10:57:28
我编写了一个递归函数来迭代深层嵌套结构,如下所示:type Container struct {    Name string    Items []Item}type Item struct {    Name string    Info Info    Vals []string}// recursively reads nested struct, prints string valuesfunc 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} 
查看完整描述

1 回答

?
蝴蝶刀刀

TA贡献1801条经验 获得超8个赞

您的代码中有几个错误。

Value.Elem()首先,只有在传递的值是指针时才需要调用。当您遍历这些字段并找到一个结构类型的字段时,您递归地调用ReadStruct()它,它不会是一个指针,因此您不能调用Elem()它。

所以这样做:

val := reflect.ValueOf(st)
    if val.Kind() == reflect.Ptr {
    val = val.Elem()
}

接下来,由于您首先ReadStruct()调用reflect.ValueOf(),因此假设您必须将非反射值传递给ReadStruct()(即,不是 type 的值reflect.Value)。

但是当您遍历这些字段时,调用Value.Field(),您会得到一个reflect.Value包装字段。您必须调用Value.Interface()以从中提取非反射值,以便在递归调用中传递。

要迭代切片,只需使用Value.Len()获取切片长度,并Value.Index()获取切片的第 i个元素。

这是遍历函数的更正版本:

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

        }

    }

}

测试它:


c := &Container{

    Name: "c1",

    Items: []Item{

        {

            Name: "i1",

            Info: Info{Key: "k1", Value: "v1"},

        },

        {

            Name: "i2",

            Info: Info{Key: "k2", Value: "v2"},

        },

    },

}

ReadStruct(c)

输出(在Go Playground上尝试):


Name=c1

Name=i2

Key=k2

Value=v2

Name=i2

Key=k2

Value=v2

注意:通过使用递归调用,您正在提取和重新获取reflect.Value值。始终使用reflect.Values 会更有效率,因此您可以避免这些不必要的调用。


你可以这样做:


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

        }

    }

}

这将输出相同的。在Go Playground试试这个。



查看完整回答
反对 回复 2023-03-07
  • 1 回答
  • 0 关注
  • 83 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信