golang Reflect无法识别地图成员的标签

发布于 2025-01-14 13:35:05 字数 2529 浏览 0 评论 0原文

我想用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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

似最初 2025-01-21 13:35:05

如您所知,使用 &theValue 解析为类型 *interface{}*interface{} 类型与 *Student 类型不同,后者是您从 tryMapWithTypetraversalTag 的类型代码>.

如果您想将 *StudenttryMapWithReflect 传递到 traversalTag,您需要使用反射创建该指针值。普通的原生 Go 地址运算符 & 还不够。

当您有一个可寻址的 reflect.Value 时,您所需要做的就是调用 .Addr() 方法来获取指向可寻址值的指针,但是映射元素不可寻址,因此 reflectMap.MapIndex(key) 不可寻址。因此,不幸的是,您无法通过 reflectMap.MapIndex(key).Addr().Interface() 来获取 *Student

所以你唯一的选择是使用反射创建一个 *Student 类型的新值,将指向的值设置为映射中的值,然后返回 .Interface() 的。

func tryMapWithReflect(obj interface{}) {
    reflectMap := reflect.ValueOf(obj)
    for _, key := range reflectMap.MapKeys() {
        theValue := reflectMap.MapIndex(key).Interface()

        // allocate a new value of type *Student
        newValue := reflect.New(reflectMap.MapIndex(key).Type())

        // use Elem do dereference *Stunded
        // and then use Set to set the Student to the content of theValue
        newValue.Elem().Set(reflect.ValueOf(theValue))

        fmt.Printf("Key: %v, Value: %v, value pointer %p\n", key, newValue.Elem().Interface(), newValue.Interface())

        // return the newValue *Student
        traversalTag(newValue.Interface())
    }
}

https://go.dev/play/p/pNL2wjsOW5y


或者,只需删除 .Elem() 来自 traversalTag,然后您就不必向它传递指针。

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)
    for i := 0; i < elem.NumField(); i++ {
        fmt.Printf("Tag name %s, value %s\n", elem.Field(i).Name, elem.Field(i).Tag)
    }
}

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 to traversalTag from tryMapWithType.

If you want to pass *Student to traversalTag from tryMapWithReflect 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 therefore reflectMap.MapIndex(key) is not addressable. So, unfortunately for you, it's not possible to do reflectMap.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.

func tryMapWithReflect(obj interface{}) {
    reflectMap := reflect.ValueOf(obj)
    for _, key := range reflectMap.MapKeys() {
        theValue := reflectMap.MapIndex(key).Interface()

        // allocate a new value of type *Student
        newValue := reflect.New(reflectMap.MapIndex(key).Type())

        // use Elem do dereference *Stunded
        // and then use Set to set the Student to the content of theValue
        newValue.Elem().Set(reflect.ValueOf(theValue))

        fmt.Printf("Key: %v, Value: %v, value pointer %p\n", key, newValue.Elem().Interface(), newValue.Interface())

        // return the newValue *Student
        traversalTag(newValue.Interface())
    }
}

https://go.dev/play/p/pNL2wjsOW5y


Alternatively, just drop the .Elem() from the traversalTag and then you don't have to pass pointers to it.

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)
    for i := 0; i < elem.NumField(); i++ {
        fmt.Printf("Tag name %s, value %s\n", elem.Field(i).Name, elem.Field(i).Tag)
    }
}

https://go.dev/play/p/EwJ5e0uc2pd

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文