使用反射读取嵌套结构

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


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


扬帆大鱼
浏览 76回答 1
1回答

Smart猫小萌

代码中存在几个错误。首先,如果传递的值是指针,则只需调用&nbsp;Value.Elem()。当你迭代这些字段并找到一个结构类型的字段,并且你用它递归地调用它时,那将不是一个指针,因此你不能调用它。ReadStruct()Elem()所以这样做是这样的:val := reflect.ValueOf(st)if val.Kind() == reflect.Ptr {&nbsp; &nbsp; val = val.Elem()}接下来,既然你从调用反射开始。ValueOf(),即假定您必须将非反射值传递给(即,不是反射类型的值)。值)。ReadStruct()ReadStruct()但是,当您迭代字段并调用&nbsp;Value.Field()&nbsp;时,您将获得一个包装字段。您必须调用&nbsp;Value.Interface()&nbsp;来提取非反射值,以便在递归调用中传递。reflect.Value要循环访问切片,只需使用值.Len()&nbsp;获取切片长度,并使用 Value.Index()&nbsp;获取切片的第&nbsp;i 个元素。以下是遍历函数的更正版本:// 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游乐场上尝试):Name=c1Name=i2Key=k2Value=v2Name=i2Key=k2Value=v2注意:通过使用递归调用,可以提取和重新获取值。始终使用 s 会更有效,因此您可以避免这些不必要的调用。reflect.Valuereflect.Value这是你如何做到这一点: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游乐场上试试这个。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go