golang Reflect无法识别地图成员的标签
我想用reflect提取struct的map成员标签,但我发现如果从MapIndex检索成员的值,它的类型将被识别为“*interface{}”,因此所有类型信息都会丢失,没有提到reflect可以提取详细信息信息。
package main
import (
"fmt"
"reflect"
)
type Student struct {
Sname string `MyTag:"student-name"`
}
type Teacher struct {
Name string `MyTag:"teacher-name"`
Students map[string]Student `MyTag:"teacher-students"`
}
var sam = Teacher{
Name: "Sam",
Students: map[string]Student{
"Sen": {
Sname: "Sen",
},
},
}
func traversalTag(obj interface{}) {
theType := reflect.TypeOf(obj)
fmt.Printf("Traversal tag with obj: type %v, value %v\n", theType.String(), obj)
elem := reflect.TypeOf(obj).Elem()
for i := 0; i < elem.NumField(); i++ {
fmt.Printf("Tag name %s, value %s\n", elem.Field(i).Name, elem.Field(i).Tag)
}
}
func tryMapWithType(students map[string]Student) {
for key, theValue := range students {
fmt.Printf("Key: %v, Value: %v, value pointer %p\n", key, theValue, &theValue)
traversalTag(&theValue)
}
}
func tryMapWithReflect(obj interface{}) {
reflectMap := reflect.ValueOf(obj)
for _, key := range reflectMap.MapKeys() {
theValue := reflectMap.MapIndex(key).Interface()
fmt.Printf("Key: %v, Value: %v, value pointer %p\n", key, theValue, &theValue)
traversalTag(&theValue) // Will have error
}
}
func main() {
tryMapWithType(sam.Students)
tryMapWithReflect(sam.Students)
}
运行后我收到以下错误:
Starting: C:\Users\Mento\go\bin\dlv.exe dap --check-go-version=false --listen=127.0.0.1:50308 from d:\Coding\Golang\demo
DAP server listening at: 127.0.0.1:50308
Key: Sen, Value: {Sen}, value pointer 0xc000044230
Traversal tag with obj: type *main.Student, value &{Sen}
Tag name Sname, value MyTag:"student-name"
Key: Sen, Value: {Sen}, value pointer 0xc0000442c0
Traversal tag with obj: type *interface {}, value 0xc0000442c0
panic: reflect: NumField of non-struct type interface {}
goroutine 1 [running]:
reflect.(*rtype).NumField(0xec2d20)
C:/Program Files/Go/src/reflect/type.go:1015 +0xc8
main.traversalTag({0xebd9e0, 0xc0000442c0})
d:/Coding/Golang/demo/demo.go:31 +0x1cb
main.tryMapWithReflect({0xec3d40, 0xc00007a480})
d:/Coding/Golang/demo/demo.go:48 +0x2c9
main.main()
d:/Coding/Golang/demo/demo.go:54 +0x38
Process 8716 has exited with status 2
dlv dap (11448) exited with code: 0
任何人都可以提示如何获取具有原始类型信息的映射成员的指针吗?
感谢您, 门托
I want to extract struct's map members' tag with reflect, while I found if retrieve member's value from MapIndex, the type of it will be recognized as "*interface{}" and hence all type information are lost, no mention reflect can extract detail information.
package main
import (
"fmt"
"reflect"
)
type Student struct {
Sname string `MyTag:"student-name"`
}
type Teacher struct {
Name string `MyTag:"teacher-name"`
Students map[string]Student `MyTag:"teacher-students"`
}
var sam = Teacher{
Name: "Sam",
Students: map[string]Student{
"Sen": {
Sname: "Sen",
},
},
}
func traversalTag(obj interface{}) {
theType := reflect.TypeOf(obj)
fmt.Printf("Traversal tag with obj: type %v, value %v\n", theType.String(), obj)
elem := reflect.TypeOf(obj).Elem()
for i := 0; i < elem.NumField(); i++ {
fmt.Printf("Tag name %s, value %s\n", elem.Field(i).Name, elem.Field(i).Tag)
}
}
func tryMapWithType(students map[string]Student) {
for key, theValue := range students {
fmt.Printf("Key: %v, Value: %v, value pointer %p\n", key, theValue, &theValue)
traversalTag(&theValue)
}
}
func tryMapWithReflect(obj interface{}) {
reflectMap := reflect.ValueOf(obj)
for _, key := range reflectMap.MapKeys() {
theValue := reflectMap.MapIndex(key).Interface()
fmt.Printf("Key: %v, Value: %v, value pointer %p\n", key, theValue, &theValue)
traversalTag(&theValue) // Will have error
}
}
func main() {
tryMapWithType(sam.Students)
tryMapWithReflect(sam.Students)
}
After run I got following error:
Starting: C:\Users\Mento\go\bin\dlv.exe dap --check-go-version=false --listen=127.0.0.1:50308 from d:\Coding\Golang\demo
DAP server listening at: 127.0.0.1:50308
Key: Sen, Value: {Sen}, value pointer 0xc000044230
Traversal tag with obj: type *main.Student, value &{Sen}
Tag name Sname, value MyTag:"student-name"
Key: Sen, Value: {Sen}, value pointer 0xc0000442c0
Traversal tag with obj: type *interface {}, value 0xc0000442c0
panic: reflect: NumField of non-struct type interface {}
goroutine 1 [running]:
reflect.(*rtype).NumField(0xec2d20)
C:/Program Files/Go/src/reflect/type.go:1015 +0xc8
main.traversalTag({0xebd9e0, 0xc0000442c0})
d:/Coding/Golang/demo/demo.go:31 +0x1cb
main.tryMapWithReflect({0xec3d40, 0xc00007a480})
d:/Coding/Golang/demo/demo.go:48 +0x2c9
main.main()
d:/Coding/Golang/demo/demo.go:54 +0x38
Process 8716 has exited with status 2
dlv dap (11448) exited with code: 0
Can anyone hint how to get pointer of map members with original type information?
Thanks you,
Mento
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
如您所知,使用
&theValue
解析为类型*interface{}
。*interface{}
类型与*Student
类型不同,后者是您从tryMapWithTypetraversalTag
的类型代码>.如果您想将
*Student
从tryMapWithReflect
传递到traversalTag
,您需要使用反射创建该指针值。普通的原生 Go 地址运算符&
还不够。当您有一个可寻址的
reflect.Value
时,您所需要做的就是调用.Addr()
方法来获取指向可寻址值的指针,但是映射元素不可寻址,因此reflectMap.MapIndex(key)
不可寻址。因此,不幸的是,您无法通过reflectMap.MapIndex(key).Addr().Interface()
来获取*Student
。所以你唯一的选择是使用反射创建一个 *Student 类型的新值,将指向的值设置为映射中的值,然后返回
.Interface()
的。https://go.dev/play/p/pNL2wjsOW5y
或者,只需删除
.Elem()
来自traversalTag
,然后您就不必向它传递指针。https://go.dev/play/p/EwJ5e0uc2pd
As you know using
&theValue
resolves to the type*interface{}
. The type*interface{}
is distinct from the type*Student
which is what you are passing in totraversalTag
fromtryMapWithType
.If you want to pass
*Student
totraversalTag
fromtryMapWithReflect
you need to create that pointer value using reflection. Plain native Go address operator&
just isn't enough.When you have a
reflect.Value
that is addressable all you need to do is to call the.Addr()
method to get a pointer to the addressable value, however map elements are not addressable and thereforereflectMap.MapIndex(key)
is not addressable. So, unfortunately for you, it's not possible to doreflectMap.MapIndex(key).Addr().Interface()
to get*Student
.So your only option is to use reflection to create a new value of the
*Student
type, set the pointed-to value to the value in the map, and then return the.Interface()
of that.https://go.dev/play/p/pNL2wjsOW5y
Alternatively, just drop the
.Elem()
from thetraversalTag
and then you don't have to pass pointers to it.https://go.dev/play/p/EwJ5e0uc2pd