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

使用反射读取嵌套结构

使用反射读取嵌套结构

Go
扬帆大鱼 2022-09-12 20:09:03
我编写了一个递归函数,该函数迭代深度嵌套结构,如下所示: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 回答

?
Smart猫小萌

TA贡献1911条经验 获得超7个赞

代码中存在几个错误。

首先,如果传递的值是指针,则只需调用 Value.Elem()。当你迭代这些字段并找到一个结构类型的字段,并且你用它递归地调用它时,那将不是一个指针,因此你不能调用它。ReadStruct()Elem()

所以这样做是这样的:

val := reflect.ValueOf(st)

if val.Kind() == reflect.Ptr {

    val = val.Elem()

}

接下来,既然你从调用反射开始。ValueOf(),即假定您必须将非反射值传递给(即,不是反射类型的值)。值)。ReadStruct()ReadStruct()

但是,当您迭代字段并调用 Value.Field() 时,您将获得一个包装字段。您必须调用 Value.Interface() 来提取非反射值,以便在递归调用中传递。reflect.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游乐场上尝试):


Name=c1

Name=i2

Key=k2

Value=v2

Name=i2

Key=k2

Value=v2

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


这是你如何做到这一点:


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游乐场上试试这个。


查看完整回答
反对 回复 2022-09-12
  • 1 回答
  • 0 关注
  • 75 浏览
慕课专栏
更多

添加回答

举报

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