``. gofmt не приводит их к стандартному виду, стоит руководствоваться принятыми в Go-сообществе правилами оформления тегов:snake_case или camelCase.type User struct {
ID string `json:"id" format:"uuid"`
Name string `json:"name"`
CreatedAt int64 `json:"created_at" format:"unixtime,seconds"`
} json — имя поля JSON-представления объекта (используется JSON-сериализаторами);format — дополнительные данные о формате поля (могут использоваться другими библиотеками).json, yaml, xml, protobuf. Как правило, сериализаторы не требуют обязательного указания тегов. Имя поля в результирующем формате данных будет совпадать с именем поля структуры.// Person содержит информацию о пользователе
// и описывает сериализацию в JSON и YAML.
type Person struct {
ID int
Name string `yaml:"name"`
Email string `json:"email" yaml:"email"`
} nullable). Есть пакеты, которые используют структурные теги для работы с базами данных: gorm, bun.// User демонстрирует пример описания GORM-модели.
type User struct {
ID uuid.UUID `gorm:"type:uuid;default:uuid_generate_v4()"`
Name string `gorm:"size:255"` // указывает размер поля
Email string `gorm:"type:varchar (100);unique_index"`
Phone string `gorm:"index:phone"` // создаёт индекс для этого поля
mark int `gorm:"-"` // не участвует в модели
} valid описывает тип поля и допустимые значения.type Example struct {
Name string `valid:"-"`
Email string `valid:"email"`
URL string `valid:"url"`
}
func main() {
// проверяем на валидность email и URL
result, err := govalidator.ValidateStruct(Example{
Email: "johndoe@example.com",
URL: "https://www.ya.ru",
})
if err != nil {
log.Fatal(err)
}
fmt.Println(result)
} reflect стандартной библиотеки Go.reflect определён type StructTag string со следующими публичными методами:Lookup(key string) (value string, ok bool) — возвращает значение тега по имени и флаг присутствия искомого тега в наборе.Get(key string) string — аналогичен Lookup, но не возвращает информацию о наличии тега. Если тег не найден, функция вернёт пустую строку.obj структурного типа и нужно получить теги полей соответствующей структуры:obj := MyType{}
// получаем тип переменной — st имеет тип reflect.Type
st := reflect.TypeOf(obj)
// Type.NumField() — возвращает количество полей
for i:=0; i < st.NumField(); i++ {
// field содержит информацию о поле с i-м индексом (с 0)
field := st.Field(i)
// field.Tag имеет тип StructTag и содержит все теги i-го поля
// выводим имя поля и значение тега `json`
fmt.Println(field.Name, field.Tag.Get("json"))
} (*Type) FieldByName(name string) (StructField, bool). Вот пример того, как можно получить тег конкретного поля:package main
import (
"fmt"
"reflect"
"time"
)
type MyType struct {
User string `json:"user,omitempty" example:"Bob"`
CreatedAt time.Time `json:"created_at"`
}
const (
targetField = "User" // имя поля, о котором нужно получить информацию
targetTag = "json" // тег, значение которого нужно получить
)
func main() {
obj := MyType{}
// получаем Go-описание типа
objType := reflect.TypeOf(obj)
// ищем поле по имени
field, ok := objType.FieldByName(targetField)
if !ok {
panic(fmt.Errorf("field (%s): not found", targetField))
}
// ищем тег по имени
tagValue, ok := field.Tag.Lookup(targetTag)
if !ok {
panic(fmt.Errorf("tag (%s) for field (%s): not found", targetTag, targetField))
}
fmt.Printf("Значение тега %s поля %s: %s\n", targetTag, targetField, tagValue)
fmt.Printf("Теги поля %s: %s\n", targetField, field.Tag)
} Значение тега json поля User: user,omitempty
Теги поля User: json:"user,omitempty" example:"Bob" "name" и "omitempty" для поля User), нужно дополнительно разобрать строку — это можно сделать функцией strings.Split(tagValue, ",").json стандартной библиотеки. При операциях сериализации код пакета пробегает по всем полям структуры, а также рекурсивно по вложенным структурам, если они есть. Чтобы оптимизировать этот процесс, пакет использует кеширование: если он уже разобрал один раз определённый тип, то последующие операции сериализации с использованием этого типа (или его составляющих) пакет выполняет быстрее.nullable и так далее. Для формирования SQL-запроса библиотека должна пробежать по всем полям типа и собрать нужные ей аннотации — например, получить все возможные имена столбцов для SELECT-запроса. func init()) — спарсить все теги один раз на старте, создать метахранилище и использовать готовые данные для формирования запроса или других операций.reflect происходит в рантайме. Способа оптимизировать эту операцию нет. Поэтому любая работа с рефлексией — это всегда поиск баланса между удобством (generic code) и скоростью.json-тегов. Код с таким типом скомпилируется, но сериализация будет работать не так, как ожидается, или приведёт к панике:type MyStruct struct {
// дубликат тега
ID int `json:"id" yaml:"id" json:"myid"`
// разделение запятыми, а не пробелом
Name string `json:"name", yaml:"name"`
// лишние пробелы
Key string `json: "key" yaml :"key"`
// перепутаны виды кавычек
Company string "json:`key`"
// encoding/json игнорирует неэкспортируемые поля
hidden string `json:"hidden"`
} reflect.StructTag по принципу «кто первый, тот и прав»: ищется первое вхождение искомого тега в набор.\ в значении тега останавливают парсинг строки, выполняемый методом StructTag.Lookup(). Разработчик может не придерживаться принятых правил, но тогда он должен разбирать теги, не используя этот метод. Это плохая практика, лучше её избегать.-structtag — она проверит, согласуются ли используемые теги с методом reflect.StructTag.Get().reflect.json, который использует структурные теги для определения параметров JSON-преобразования.