Certamente, se você já escreveu ou apenas parou para ler algum código em Go, com certeza já deve ter encontrado algo escrito ao lado do nome de um campo em uma struct, mas afinal, o que é aquilo???
type People struct {
ID uuid.UUID `json:"id,omitempty"`
Name string `json:"name,omitempty"`
Age uint8 `json:"age,omitempty"`
BirthYear uint16 `json:"birth_year,omitempty"`
}
Isso são anotações de campos de uma estrutura, conhecidas como struct field tags, as quais podem adicionar ou não comportamentos extras aos campos.
Estrutura de uma tag
- Toda tag é composta por:
` `
- multiline string.chave:valor
- semelhante a um map, o valor geralmente é uma string.
- no final ficamos com:
`chave:"valor"`
Cada pacote implementa a leitura dos valores de forma diferente. O pacote encoding define como separação de valores ,
, enquanto o GORM define como separador o ;
.
Exemplo:
type Author struct {
ID uuid.UUID `json:"id,omitempty"`
Name string `json:"name,omitempty"`
}
assim como as tags podem ter diversos valores, cada campo pode ter diversas tags também.
Exemplo:
type Author struct {
ID uuid.UUID `json:"id,omitempty" gorm:"primaryKey;type:uuid;default:gen_random_uuid()"`
Name string `json:"name,omitempty" binding:"required,min=2" gorm:"type:varchar(255);column:name;unique;not null"`
}
No exemplo acima, ID
tem as tags json
e gorm
, enquanto Name
tem as tags json
, gorm
e binding
(faz parte do pacote validate).
Usando o pacote encoding/json para entender mais sobre as tags
Imagine uma requisição a uma API onde você tem que passar um JSON no corpo da requisição. Esse JSON será processado pelo seu código, mas o padrão de nomenclatura dos campos do JSON pode ser diferente do definido em seu código. Você pode ver comumente pela web o uso de camelCase, mas também outros usando snake_case. Portanto, é necessário dizer ao seu código como lidar com esse tipo de problema.
package main
import (
"encoding/json"
"fmt"
"log"
)
type People struct {
Name string `json:"name,omitempty"`
Age uint8 `json:"age,omitempty"`
BirthYear uint16 `json:"birth_year,omitempty"`
}
func main() {
// Simula entrada de um JSON binário (serialização).
p1 := People{
Name: "John",
BirthYear: 2004,
}
bJson, err := json.Marshal(p1)
if err != nil {
log.Fatal(err)
}
var jsonMap map[string]any
// Desserializa o JSON binário dentro de um map.
if err := json.Unmarshal(bJson, &jsonMap); err != nil {
log.Fatal(err)
}
var jsonStruct People
// Desserializa o JSON binário dentro de uma struct.
if err := json.Unmarshal(bJson, &jsonStruct); err != nil {
log.Fatal(err)
}
fmt.Println(jsonMap, jsonStruct)
}
output
map[birth_year:2004 name:John] {John 0 2004}
Quando o pacote json fez a desserialização no map temos um map com as chaves birth_year
e name
, mas sem a presença do campo age
devido a anotação json:"omitempty"
, agora a desserialização na struct o pacote json já sabia qual campo atribuir a birthYear
, pois BirthYear contém a anotação json:"birth_year"
Criando uma tag personalizada
Para criar uma custom tag, necessitamos usar o pacote reflect
para ter acesso não só as tags, mas também aos campos.
package main
import (
"fmt"
"reflect"
"strings"
)
type People struct {
Name string `json:"name,omitempty" myTag:"upper"`
Age uint8 `json:"age,omitempty"`
BirthYear uint16 `json:"birth_year,omitempty"`
}
func (p *People) Validate() {
t := reflect.TypeOf(*p)
// Itéra sobre o número de campos na struct
for i := range t.NumField() {
// Verifica se o campo contém a tag myTag
if _, ok := t.Field(i).Tag.Lookup("myTag"); ok == true {
fieldName := t.Field(i).Name
// pega o valor e o define com comportamento upper
addr := reflect.ValueOf(p).Elem().FieldByName(fieldName)
addr.SetString(strings.ToUpper(addr.String()))
}
}
}
func main() {
p1 := People{
Name: "john",
Age: 21,
BirthYear: 2004,
}
fmt.Println(p1)
p1.Validate()
fmt.Println(p1)
}
output
{john 21 2004}
{JOHN 21 2004}
Para esse exemplo, usei myTag
, que tem o valor upper
que transforma o campo Name
para maiúsculo. Como o intuito do exemplo é apenas mostrar como usar uma custom tag, fiz o código mais simples possível, portanto algumas partes necessárias de verificação no código não foram implementadas.
Observação
O pacote
reflect
deve ser usado com cautela, pois pode afetar a performance da aplicação.
Top comments (0)