Как получить имена json-полей структуры в golang?

Каков способ получить имена 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

Ответ 1

Вы используете тип 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 поддерживает больше, чем просто имена полей, такие как omitempty или string, поэтому, если вам нужен подход, который также позаботится об этом, необходимо внести дополнительные улучшения в функцию 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

        if jsonTag := t.Tag.Get("json"); jsonTag != "" && jsonTag != "-" {
            if commaIdx := strings.Index(jsonTag, ","); commaIdx > 0 {
                fieldName = jsonTag[:commaIdx]
            }
        }


        fmt.Println(fieldName)
    }
}

Ответ 2

Вместо StructField Name вы можете использовать Tag для получения объекта StructTag. См.: https://golang.org/pkg/reflect/#StructTag

Затем вы можете использовать методы StructTag Get или Lookup для получения тега 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"))
    }
}

Посмотрите на игровая площадка.

Ответ 4

Не Name, который вы ищете. Что вы ищете Tag

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"))
    }
}

Ответ 5

обновленная версия с универсальным интерфейсом в качестве параметра:

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)
    }
}