Struct Tags & Custom Tag Parsing
Tags de struct anexam metadados declarativos a campos.
Busque em todas as páginas da documentação
Tags de struct anexam metadados declarativos a campos.
Bibliotecas leem essas strings em tempo de execução com reflection ou em tempo de compilação com ferramentas AST.
Esta página mostra como analisar tags json, db e validate e construir um pequeno binder personalizado.
Tags de struct são strings entre crases (backticks) após a declaração de um campo.
O pacote reflect as expõe como valores StructTag com Get e Lookup.
Parsers de produção validam a sintaxe, suportam múltiplas chaves e armazenam planos de campo por tipo em cache.
Para caminhos de alta performance, a geração de código lê as mesmas tags da origem e emite atribuições diretas.
Cartão de referência rápida para análise de tags.
// Declaração de campo
Name string `json:"name,omitempty" db:"user_name" validate:"required,min=2"`
// Lê uma chave
tag := field.Tag.Get("json") // "name,omitempty"
name, opts, _ := strings.Cut(tag, ",")
// Lookup com flag ok
v, ok := field.Tag.Lookup("validate") // "required,min=2", trueQuando usar isso:
go generateUm parser mínimo de tag env que preenche uma struct a partir de um mapa.
package main
import (
"fmt"
"reflect"
"strconv"
"strings"
)
type Server struct {
Host string `env:"HOST" validate:"required"`
Port int `env:"PORT" validate:"min=1"`
}
type fieldPlan struct {
index int
key string
kind reflect.Kind
}
func plans(t reflect.Type) ([]fieldPlan, error) {
if t.Kind() != reflect.Struct {
return nil, fmt.Errorf("precisa de struct")
}
var out []fieldPlan
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if !f.IsExported() {
continue
}
key, ok := f.Tag.Lookup("env")
if !ok || key == "" {
continue
}
out = append(out, fieldPlan{index: i, key: key, kind: f.Type.Kind()})
}
return out, nil
}
func bind(ptr any, env map[string]string) error {
v := reflect.ValueOf(ptr)
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
return fmt.Errorf("ptr deve ser *struct")
}
v = v.Elem()
t := v.Type()
fp, err := plans(t)
if err != nil {
return err
}
for _, p := range fp {
raw, ok := env[p.key]
if !ok {
continue
}
fv := v.Field(p.index)
switch p.kind {
case reflect.String:
fv.SetString(raw)
case reflect.Int:
n, err := strconv.Atoi(raw)
if err != nil {
return fmt.Errorf("%s: %w", p.key, err)
}
fv.SetInt(int64(n))
default:
return fmt.Errorf("tipo não suportado %s para %s", p.kind, p.key)
}
}
return nil
}
func main() {
s := &Server{}
err := bind(s, map[string]string{"HOST": "0.0.0.0", "PORT": "3000"})
fmt.Println(s, err)
}O que isso demonstra:
Tag.Lookup distingue chaves ausentes de valores vaziosreflect.Type.Field(i) retorna StructField com Name, Type, Tag e offset.StructTag.Get(key) retorna a porção do valor antes da primeira vírgula para aquela chave.`json:"x" xml:"x"`.| Chave | Consumidor Típico | Formato do Valor |
|---|---|---|
json | encoding/json | name,omitempty,string |
db | sqlx, GORM | nome da coluna, opções |
validate | go-playground/validator | regras unidas por vírgulas |
yaml | gopkg.in/yaml.v3 | nome yaml |
env | binders personalizados | nome da variável de ambiente |
Divide por vírgulas, depois analisa opções chave=valor:
func parseRules(tag string) map[string]string {
rules := map[string]string{}
for _, part := range strings.Split(tag, ",") {
k, v, ok := strings.Cut(strings.TrimSpace(part), "=")
if !ok {
rules[k] = "true"
continue
}
rules[k] = v
}
return rules
}// Armazena planos em cache por reflect.Type em um sync.Map para frameworks
var cache sync.Map // chave: reflect.Type, valor: []fieldPlanjson:"name" causam sobrescritas silenciosas em encoding/json. Correção: Execute go vet e linters personalizados no CI.fieldPlan em cache ficam obsoletas se você recarregar tipos em plugins de longa duração. Correção: Use a identidade do tipo como chave do cache e documente os limites de recarga dinâmica.ok do Lookup - Get retorna "" tanto para ausentes quanto para vazios. Correção: Use Lookup quando a presença for importante.
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Mapeamento escrito à mão | Um ou dois DTOs | Dezenas de structs similares |
go generate a partir de tags | Vinculação de alta performance | Protótipo rápido ainda em mudança de formato |
| Embutimento de struct + métodos | Comportamento ligado a tipos | Objetos puramente de transferência de dados |
Decodificador mapstructure | Mapas de configuração flexíveis | Você precisa de tags de struct estritas por campo |
String entre crases com pares chave:"valor" separados por espaço, ex: `json:"id"`.
Valores são sempre strings entre aspas.
Ele reflete sobre o tipo e lê a chave json para nomes de campos e opções como omitempty.
Sim.
Qualquer chave é válida; seu parser ou gerador a interpreta.
Alguns analisadores sinalizam tags JSON incompatíveis ou tags de struct suspeitas.
Habilite govet e linters de tags de struct no CI.
db nomeia colunas SQL para ORMs e sqlx; json nomeia campos de comunicação.
A mesma struct frequentemente carrega ambas.
Percorra o tipo uma vez, armazene planos em cache, reutilize em cada bind.
Reflection completa por requisição só é aceitável em caminhos frios.
Sim.
go/ast ou go/types inspecionam tags de campo no tempo de geração de código.
O JSON do stdlib ignora opções desconhecidas; parsers personalizados devem retornar erros na inicialização, não por requisição.
Não.
São metadados de implementação, não comentários de documentação.
Teste structs em tabelas com diferentes combinações de tags e afirme os valores vinculados e mensagens de erro.
Versões de Stack: Esta página foi escrita para Go 1.26.x (padrão GC Green Tea, go fix modernizers - verifique o patch na compilação), chi (última versão - verifique na compilação), gin (última versão - verifique na compilação), echo (última versão - verifique na compilação), google.golang.org/grpc (última versão - verifique na compilação), sigs.k8s.io/controller-runtime (última versão - verifique na compilação), kubebuilder (última versão - verifique na compilação), tinygo (última versão - verifique os alvos de placa na compilação), wazero (última versão - verifique na compilação) e golangci-lint (última versão - verifique o conjunto de linters na compilação).
Revisado por Chris St. John·Última atualização: 19 de jul. de 2026