使用反射读取嵌套结构
Reading nested structure using reflection
我编写了一个递归函数来迭代深层嵌套结构,如下所示:
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
}
您的代码中有几个错误。
首先,如果传递的值是指针,则只需调用 Value.Elem()
。当您遍历这些字段并找到一个结构类型的字段时,您递归地调用 ReadStruct()
,它不会是一个指针,因此您不能调用 Elem()
。
所以这样做:
val := reflect.ValueOf(st)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
接下来,由于您通过调用 reflect.ValueOf()
, that assumes you have to pass non-reflect values to ReadStruct()
(that is, not values of type reflect.Value
) 开始 ReadStruct()
。
但是当您遍历字段时,调用 Value.Field()
, you get a reflect.Value
wrapping the field. You have to call Value.Interface()
从中提取非反射值,以在递归调用中传递。
要迭代切片,只需使用 Value.Len()
to get the slice length, and Value.Index()
获取切片的第 ith 个元素。
这是您的遍历函数的更正版本:
// 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.Value
s 会更有效率,因此您可以避免这些不必要的调用。
你可以这样做:
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.
上试试这个
我编写了一个递归函数来迭代深层嵌套结构,如下所示:
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
}
您的代码中有几个错误。
首先,如果传递的值是指针,则只需调用 Value.Elem()
。当您遍历这些字段并找到一个结构类型的字段时,您递归地调用 ReadStruct()
,它不会是一个指针,因此您不能调用 Elem()
。
所以这样做:
val := reflect.ValueOf(st)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
接下来,由于您通过调用 reflect.ValueOf()
, that assumes you have to pass non-reflect values to ReadStruct()
(that is, not values of type reflect.Value
) 开始 ReadStruct()
。
但是当您遍历字段时,调用 Value.Field()
, you get a reflect.Value
wrapping the field. You have to call Value.Interface()
从中提取非反射值,以在递归调用中传递。
要迭代切片,只需使用 Value.Len()
to get the slice length, and Value.Index()
获取切片的第 ith 个元素。
这是您的遍历函数的更正版本:
// 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.Value
s 会更有效率,因此您可以避免这些不必要的调用。
你可以这样做:
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.
上试试这个