Golang Reflect не может распознать тег членов карты

Я хочу извлечь тег элементов карты структуры с помощью отражения, в то время как я обнаружил, что если получить значение члена из MapIndex, его тип будет распознан как «* интерфейс {}», и, следовательно, вся информация о типе будет потеряна, никакое упоминание об отражении не может извлекать детали Информация.

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

Может ли кто-нибудь подсказать, как получить указатель членов карты с исходной информацией о типе?

Спасибо, Менто

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
32
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Как вы знаете, использование &theValue разрешается в тип *interface{}. Тип *interface{} отличается от типа *Student, который вы передаете в traversalTag из tryMapWithType.

Если вы хотите передать *Student в traversalTag из tryMapWithReflect, вам нужно создать это значение указателя с помощью отражения. Простого нативного оператора адреса 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

это или изменение типа поля на map[string]*Student также помогает

blackgreen 16.03.2022 14:56

Другие вопросы по теме