如何在golang中获取结构体的json字段名?

33

如何获取此结构体中的JSON字段名?

type example struct {
    Id          int `json:"id"`
    CreatedAt   string `json:"created_at"`
    Tag         string `json:"tag"`
    Text        string `json:"text"`
    AuthorId    int `json:"author_id"`
}

我尝试使用这个函数打印出字段:

func (b example) PrintFields() {
    val := reflect.ValueOf(b)
    for i := 0; i < val.Type().NumField(); i++ {
        fmt.Println(val.Type().Field(i).Name)
    }
}

当然我懂得:

Id
CreatedAt
Tag
Text
AuthorId

但我想要类似这样的东西:

id
created_at
tag
text
author_id

请查看此答案(其中包含获取标签值的示例):Go中标签的用途是什么? - icza
7个回答

42
您可以使用StructTag类型来获取标签。我提供的文档中有示例,您可以查看,但您的代码可能类似于:
func (b example) PrintFields() {
  val := reflect.ValueOf(b)
  for i := 0; i < val.Type().NumField(); i++ {
     fmt.Println(val.Type().Field(i).Tag.Get("json"))
  }
}

注意 json 标签格式支持更多的内容,比如 omitemptystring,因此如果您需要一个能够处理这些内容的方法,应该进一步改进 PrintFields 函数:

  1. 我们需要检查 json 标签是否为 -(即 json:"-"
  2. 我们需要检查名称是否存在并将其隔离

类似于这样:

func (b example) PrintFields() {
  val := reflect.ValueOf(b)
  for i := 0; i < val.Type().NumField(); i++ {
    t := val.Type().Field(i)
    fieldName := t.Name

    switch jsonTag := t.Tag.Get("json"); jsonTag {
    case "-":
    case "":
        fmt.Println(fieldName)
    default:
        parts := strings.Split(jsonTag, ",")
        name := parts[0]
        if name == "" {
            name = fieldName
        }
        fmt.Println(name)
    }
  }
}

3
请注意,json标签支持的不仅仅是属性名,例如<name>,omitempty,你的代码将因为返回的内容超出字段名而导致失败。 - Victor

5

可以使用Tag替代StructFieldName来获取StructTag对象。 请参考:https://golang.org/pkg/reflect/#StructTag

然后,您可以使用StructTagGetLookup方法来获取json标签:

使用Get

func (b example) PrintFields() {
    val := reflect.ValueOf(b)
    for i := 0; i < val.Type().NumField(); i++ {
        // prints empty line if there is no json tag for the field
        fmt.Println(val.Type().Field(i).Tag.Get("json"))
    }
}

使用 Lookup:

func (b example) PrintFields() {
    val := reflect.ValueOf(b)
    for i := 0; i < val.Type().NumField(); i++ {
        // skips fields without json tag
        if tag, ok := val.Type().Field(i).Tag.Lookup("json"); ok {
            fmt.Println(tag)
        }
    }
}

3

使用:

func (b example) PrintFields() {
    val := reflect.ValueOf(b)
    t := val.Type()
    for i := 0; i < t.NumField(); i++ {
        fmt.Println(t.Field(i).Tag.Get("json"))
    }
}

请在playground中查看。

由于代码片段中未使用 val,因此现在可以简化为 t := reflect.TypeOf(b) - https://go.dev/play/p/tqtlOkmA20x - Marcus

3

一个具有通用接口参数的更新版本:

func PrintFields(b interface{}) {
    val := reflect.ValueOf(b)
    for i := 0; i < val.Type().NumField(); i++ {
        t := val.Type().Field(i)
        fieldName := t.Name

        if jsonTag := t.Tag.Get("json"); jsonTag != "" && jsonTag != "-" {
            // check for possible comma as in "...,omitempty"
            var commaIdx int
            if commaIdx = strings.Index(jsonTag, ","); commaIdx < 0 {
                commaIdx = len(jsonTag)
            }
            fieldName = jsonTag[:commaIdx]
        }
        fmt.Println(fieldName)
    }
}

2
func (e example) GetJsonField() []string {
    b := example{}
    marshaled, _ := json.Marshal(b)
    m := make(map[string]interface{})
    _ = json.Unmarshal(marshaled, &m)
    
    result := make([]string, 0)
    for k := range m {
        result = append(result, k)
    }
    return result
}

1

你要找的不是 名称,而是 标签

func (b example) PrintFields() {
    val := reflect.ValueOf(b)
    for i := 0; i < val.Type().NumField(); i++ {
        fmt.Println(val.Type().Field(i).Tag.Get("json"))
    }
}

0

使用结构体字段名获取Json字段名

val := reflect.ValueOf(structobject)    
field, _ := val.Type().FieldByName("Id")
fmt.Println(field.Tag.Get("json"))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接