1

我编写了一个递归函数,它遍历深层嵌套结构,如下所示:

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
} 
4

1 回答 1

3

您的代码中有几个错误。

首先,您只需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值。始终使用 s 会更有效reflect.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 Playground上试试这个。

于 2021-07-08T10:50:49.293 回答