Как использовать теги в Go?
В спецификации языка Go упоминается краткий обзор тегов:
За объявлением поля может следовать необязательный строковый литерал, который становится атрибутом для всех полей в соответствующем объявлении поля. Теги становятся видимыми через интерфейс отражения, но в остальном игнорируются.
// A struct corresponding to the TimeStamp protocol buffer. // The tag strings define the protocol buffer field numbers. struct { microsec uint64 "field 1" serverIP6 uint64 "field 2" process string "field 3" }
Это очень короткое объяснение IMO, и мне было интересно, если кто-нибудь может предоставить мне, как будут использоваться эти теги?
4 ответа
Тег для поля позволяет вам прикрепить метаинформацию к полю, которую можно получить с помощью отражения. Обычно он используется для предоставления информации о преобразовании того, как структурное поле кодируется или декодируется из другого формата (или сохраняется / извлекается из базы данных), но вы можете использовать его для хранения любой мета-информации, которую вы хотите, либо предназначенной для другого пакет или для собственного использования.
Как указано в документации reflect.StructTag
по соглашению значение строки тега представляет собой разделенный пробелами список key:"value"
пары, например:
type User struct {
Name string `json:"name" xml:"name"`
}
key
обычно обозначает пакет, который последующий "value"
для, например, json
ключи обрабатываются / используются encoding/json
пакет.
Если многократная информация должна быть передана в "value"
обычно это указывается через запятую (','
), например
Name string `json:"name,omitempty" xml:"name"`
Обычно значение тире ('-'
) для "value"
означает исключить поле из процесса (например, в случае json
это означает не маршалировать и не демаршировать это поле).
Пример доступа к вашим пользовательским тегам с помощью отражения
Мы можем использовать отражение (reflect
пакет) для доступа к значениям тегов структурных полей. В основном нам нужно приобрести Type
нашей структуры, а затем мы можем запросить поля, например, с Type.Field(i int)
или же Type.FieldByName(name string)
, Эти методы возвращают значение StructField
который описывает / представляет поле структуры; а также StructField.Tag
это значение типа StructTag
который описывает / представляет значение тега.
Ранее мы говорили о "конвенции". Это соглашение означает, что если вы будете следовать ему, вы можете использовать StructTag.Get(key string)
метод, который анализирует значение тега и возвращает вам "value"
из key
вы указываете. Соглашение реализовано / встроено в это Get()
метод. Если вы не будете следовать соглашению, Get()
не сможет разобрать key:"value"
пары и найти то, что вы ищете. Это тоже не проблема, но тогда вам нужно реализовать собственную логику синтаксического анализа.
Также есть StructTag.Lookup()
(был добавлен в Go 1.7), который "как Get()
но отличает тег, не содержащий данный ключ, от тега, связывающего пустую строку с данным ключом ".
Итак, давайте посмотрим на простой пример:
type User struct {
Name string `mytag:"MyName"`
Email string `mytag:"MyEmail"`
}
u := User{"Bob", "bob@mycompany.com"}
t := reflect.TypeOf(u)
for _, fieldName := range []string{"Name", "Email"} {
field, found := t.FieldByName(fieldName)
if !found {
continue
}
fmt.Printf("\nField: User.%s\n", fieldName)
fmt.Printf("\tWhole tag value : %q\n", field.Tag)
fmt.Printf("\tValue of 'mytag': %q\n", field.Tag.Get("mytag"))
}
Вывод (попробуйте на Go Playground):
Field: User.Name
Whole tag value : "mytag:\"MyName\""
Value of 'mytag': "MyName"
Field: User.Email
Whole tag value : "mytag:\"MyEmail\""
Value of 'mytag': "MyEmail"
GopherCon 2015 провел презентацию о структурных тегах под названием:
Теги Многогранности Структуры (слайд) (и видео)
Вот список часто используемых ключей тегов:
json
- используетсяencoding/json
пакет, подробно описано вjson.Marshal()
xml
- используетсяencoding/xml
пакет, подробно описано вxml.Marshal()
bson
- используется Гобсоном, подробно наbson.Marshal()
protobuf
- использованgithub.com/golang/protobuf/proto
, подробно в пакете документовyaml
- используетсяgopkg.in/yaml.v2
пакет, подробно описано вyaml.Marshal()
db
- используетсяgithub.com/jmoiron/sqlx
пакет; также используетсяgithub.com/go-gorp/gorp
пакетorm
- используетсяgithub.com/astaxie/beego/orm
пакет, детализированный на Моделях - Beego ORMgorm
- используетсяgithub.com/jinzhu/gorm
пакет, примеры можно найти в их документе: Моделиvalid
- используетсяgithub.com/asaskevich/govalidator
пакет, примеры можно найти на странице проектаdatastore
- использованappengine/datastore
(Платформа Google App Engine, служба хранилища данных), подробно описано в разделе "Свойства".schema
- использованgithub.com/gorilla/schema
заполнитьstruct
со значениями формы HTML, подробно описано в пакете документовasn
- используетсяencoding/asn1
пакет, подробно описано вasn1.Marshal()
а такжеasn1.Unmarshal()
csv
- используетсяgithub.com/gocarina/gocsv
пакет
Вот очень простой пример использования тегов с encoding/json
пакет для управления интерпретацией полей при кодировании и декодировании:
Попробуйте вживую: http://play.golang.org/p/BMeR8p1cKf
package main
import (
"fmt"
"encoding/json"
)
type Person struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
MiddleName string `json:"middle_name,omitempty"`
}
func main() {
json_string := `
{
"first_name": "John",
"last_name": "Smith"
}`
person := new(Person)
json.Unmarshal([]byte(json_string), person)
fmt.Println(person)
new_json, _ := json.Marshal(person)
fmt.Printf("%s\n", new_json)
}
// *Output*
// &{John Smith }
// {"first_name":"John","last_name":"Smith"}
Пакет json может посмотреть на теги для поля и узнать, как отобразить поле структуры json <=>, а также дополнительные параметры, например, следует ли игнорировать пустые поля при сериализации обратно в json.
По сути, любой пакет может использовать отражение в полях для просмотра значений тегов и воздействия на эти значения. Немного больше информации о них в отражающей упаковке
http://golang.org/pkg/reflect/:
По соглашению строки тегов представляют собой конкатенацию необязательно разделенных пробелами пар "значение". Каждый ключ является непустой строкой, состоящей из неуправляемых символов, кроме пробела (U+0020 ' '), кавычки (U+0022 '"') и двоеточия (U+003A ':'). Каждое значение заключено в кавычки используя символы "+" U + 0022 и буквенный синтаксис строки Go.
Это своего рода спецификации, которые определяют, как пакеты обрабатываются с помеченным полем.
например:
type User struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
}
тег json сообщает json
пакет, который упорядочивает вывод следующего пользователя
u := User{
FirstName: "some first name",
LastName: "some last name",
}
было бы так:
{"first_name":"some first name","last_name":"some last name"}
другой пример gorm
теги пакета объявляют, как должна выполняться миграция базы данных:
type User struct {
gorm.Model
Name string
Age sql.NullInt64
Birthday *time.Time
Email string `gorm:"type:varchar(100);unique_index"`
Role string `gorm:"size:255"` // set field size to 255
MemberNumber *string `gorm:"unique;not null"` // set member number to unique and not null
Num int `gorm:"AUTO_INCREMENT"` // set num to auto incrementable
Address string `gorm:"index:addr"` // create index with name `addr` for address
IgnoreMe int `gorm:"-"` // ignore this field
}
В этом примере для поля Email
с тегом gorm мы объявляем, что соответствующий столбец в базе данных для поля электронной почты должен иметь тип varchar и максимальную длину 100, а также иметь уникальный индекс.
другой пример binding
теги, которые чаще всего используются в gin
пакет.
type Login struct {
User string `form:"user" json:"user" xml:"user" binding:"required"`
Password string `form:"password" json:"password" xml:"password" binding:"required"`
}
var json Login
if err := c.ShouldBindJSON(&json); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
тег привязки в этом примере подсказывает пакету gin, что данные, отправляемые в API, должны иметь поля пользователя и пароля, поскольку эти поля помечены как требуемые.
Таким образом, обычно теги - это данные, которые пакетам требуется знать, как они должны обращаться с данными различных типов структур, и лучший способ познакомиться с тегами, которые нужны пакету, - ПОЛНОСТЬЮ ПРОЧИТАТЬ ДОКУМЕНТАЦИЮ ДЛЯ ПАКЕТА.
В Go теги — это, по сути, метаданные, связанные с полем структуры. Они определяются как строковые литералы, которые появляются после имени поля в определении структуры и заключаются в обратные кавычки или двойные кавычки.
Теги в Go служат нескольким целям:
- Сериализация и десериализация. Одним из наиболее распространенных применений тегов является помощь в сериализации и десериализации данных. Например, при кодировании структуры в формате JSON кодировщик будет использовать значения тегов для определения имен ключей JSON, которые будут использоваться для каждого поля.
- Отражение: пакет отражения Go позволяет программам проверять и изменять структуру программы во время выполнения. Теги можно использовать для предоставления дополнительной информации о полях структуры, доступ к которой можно получить посредством отражения.
- Проверка. Теги можно использовать для проверки данных, хранящихся в структуре. Например, тег может указывать, что определенное поле должно быть действительным адресом электронной почты или что оно должно быть неотрицательным целым числом.
- Документация. Теги можно использовать для предоставления дополнительной документации о полях структуры. Это может быть полезно для таких инструментов, как godoc, которые создают документацию для программ Go на основе комментариев исходного кода и других метаданных.