Search code examples
gostructreflectionintrospection

Get struct Tag with reflection - Error: type reflect.Value has no field or method Tag


Let's say we have this PoC:

package main

import (
        "fmt"
        "reflect"
        "strings"
)

type MyStruct struct {
        Str string `gorm:"size:10" json:"string"`
}

func main() {
        aStruct := MyStruct{"Hello"}

        s := reflect.ValueOf(&aStruct).Elem()
        if s.Kind() == reflect.Struct {
                for i := 0; i < s.NumField(); i++ {
                        field := s.Field(i)
                        if field.IsValid() {
                                if field.CanSet() {
                                        fmt.Printf("%T\n", field)  // reflect.Value, need reflect.StructField
                                        gormTag := field.Tag.Get("gorm")  // Compile ERROR HERE
                                        gormSize := strings.Split(gormTag, "size:")[1]
                                        fmt.Println(gormSize)
                                }
                        }
                }
        }
}

The error is:

go run test1.go 
# command-line-arguments
./test1.go:22:22: field.Tag undefined (type reflect.Value has no field or method Tag)

Tested with go 1.14.6 and go 1.15.2.

In my understanding I need to cast (or get from) reflect.Value to reflect.StructField Any ideas how to do that?


Solution

  • The tags belong to the type of fields of the struct, not to the values of the struct type.

    reflect.ValueOf() returns you the wrapper of the value, not its type. To get the tag value, you need to start from the wrapper of the type, e.g. acquired with reflect.TypeOf():

    t := reflect.TypeOf(&aStruct).Elem()
    

    And the type wrapper of the field in the loop (which will be of type reflect.StructTag):

    field := s.Field(i)
    fieldt := t.Field(i)
    

    And use fieldt to get the tag:

    gormTag := fieldt.Tag.Get("gorm") // No error, this works
    

    With this addition, output will be (try it on the Go Playground):

    reflect.Value
    10
    

    See related: What are the use(s) for tags in Go?