Introdução
As estruturas, ou structs, são usadas para reunir várias informações em uma unidade. Essas coleções de informações são usadas para descrever conceitos de nível superior, como um Endereço
composto por Rua
, Cidade
, Estado
e Código Postal
. Quando você lê essas informações de sistemas como bancos de dados ou APIs, pode usar tags de struct para controlar como essas informações são atribuídas aos campos de uma struct. As tags de struct são pequenos metadados anexados aos campos de uma struct que fornecem instruções para outro código Go que trabalha com a struct.
Como é uma Tag de Struct?
As tags de struct em Go são anotações que aparecem após o tipo em uma declaração de struct Go. Cada tag é composta por strings curtas associadas a algum valor correspondente.
A struct tag looks like this, with the tag offset with backtick `
characters:
Outro código Go é então capaz de examinar essas structs e extrair os valores atribuídos a chaves específicas que solicita. As tags de struct não têm efeito na operação do seu código sem código adicional que as examine.
Experimente este exemplo para ver como as tags de estrutura se parecem e que, sem código de outro pacote, elas não terão efeito.
Isso resultará em:
OutputHi! My name is Sammy
Este exemplo define um tipo User
com um campo Name
. O campo Name
recebeu uma tag de estrutura de example:"name"
. Nos referimos a esta tag específica na conversa como a “tag de estrutura de exemplo” porque ela usa a palavra “exemplo” como sua chave. A tag de estrutura example
tem o valor "name"
para o campo Name
. No tipo User
, também definimos o método String()
exigido pela interface fmt.Stringer
. Isso será chamado automaticamente quando passamos o tipo para fmt.Println
e nos dá a chance de produzir uma versão formatada do nosso struct.
Dentro do corpo de main
, criamos uma nova instância do nosso tipo User
e a passamos para fmt.Println
. Mesmo que a struct tenha uma tag de estrutura presente, vemos que isso não tem efeito na operação deste código Go. Ele se comportará exatamente da mesma forma se a tag de estrutura não estiver presente.
Para usar tags de estrutura para realizar algo, outro código Go deve ser escrito para examinar structs em tempo de execução. A biblioteca padrão possui pacotes que usam tags de estrutura como parte de sua operação. O mais popular destes é o pacote encoding/json
.
Codificação JSON
O JavaScript Object Notation (JSON) é um formato textual para codificar coleções de dados organizados sob diferentes chaves de string. É comumente usado para comunicar dados entre diferentes programas, pois o formato é simples o suficiente para que bibliotecas existam para decodificá-lo em muitas linguagens diferentes. O seguinte é um exemplo de JSON:
{
"language": "Go",
"mascot": "Gopher"
}
Este objeto JSON contém duas chaves, language
e mascot
. Após essas chaves estão os valores associados. Aqui, a chave language
tem um valor de Go
e mascot
é atribuído o valor Gopher
.
O codificador JSON na biblioteca padrão faz uso de tags de struct como anotações indicando ao codificador como você gostaria de nomear seus campos na saída JSON. Esses mecanismos de codificação e decodificação JSON podem ser encontrados no encoding/json
pacote.
Tente este exemplo para ver como o JSON é codificado sem tags de struct:
Isto irá imprimir a seguinte saída:
Output{
"Name": "Sammy the Shark",
"Password": "fisharegreat",
"CreatedAt": "2019-09-23T15:50:01.203059-04:00"
}
Definimos uma estrutura descrevendo um usuário com campos incluindo seu nome, senha e o momento em que o usuário foi criado. Dentro da função main
, criamos uma instância deste usuário fornecendo valores para todos os campos exceto PreferredFish
(Sammy gosta de todos os peixes). Em seguida, passamos a instância de User
para a função json.MarshalIndent
. Isso é usado para que possamos ver mais facilmente a saída JSON sem usar uma ferramenta de formatação externa. Esta chamada pode ser substituída por json.Marshal(u)
para imprimir JSON sem nenhum espaço adicional. Os dois argumentos adicionais para json.MarshalIndent
controlam o prefixo para a saída (que omitimos com a string vazia) e os caracteres a serem usados para a indentação, que aqui são dois caracteres de espaço. Quaisquer erros produzidos por json.MarshalIndent
são registrados e o programa termina usando os.Exit(1)
. Finalmente, convertemos o []byte
retornado de json.MarshalIndent
para uma string
e passamos a string resultante para fmt.Println
para impressão no terminal.
Os campos da estrutura aparecem exatamente como nomeados. Este não é o estilo JSON típico que você pode esperar, no entanto, que usa camel case para nomes de campos. Você mudará os nomes dos campos para seguir o estilo camel case neste próximo exemplo. Como você verá ao executar este exemplo, isso não funcionará porque os nomes de campo desejados entram em conflito com as regras do Go sobre nomes de campos exportados.
Isto apresentará a seguinte saída:
Output{}
Nesta versão, alteramos os nomes dos campos para camel case. Agora Name
é name
, Password
é password
, e finalmente CreatedAt
é createdAt
. No corpo de main
, alteramos a instanciação da nossa estrutura para usar esses novos nomes. Em seguida, passamos a estrutura para a função json.MarshalIndent
como antes. A saída, desta vez, é um objeto JSON vazio, {}
.
Para camel casear os campos corretamente, o primeiro caractere deve ser em minúsculas. Embora o JSON não se importe com como você nomeia seus campos, o Go se importa, pois isso indica a visibilidade do campo fora do pacote. Como o pacote encoding/json
é um pacote separado do pacote main
que estamos usando, precisamos colocar em maiúscula o primeiro caractere para torná-lo visível para o encoding/json
. Parece que estamos em um impasse. Precisamos de alguma forma de transmitir ao codificador JSON como gostaríamos que este campo fosse nomeado.
Usando Tags de Estrutura para Controlar a Codificação
Você pode modificar o exemplo anterior para ter campos exportados que estão devidamente codificados com nomes de campos em camel case, anotando cada campo com uma tag de struct. A tag de struct que o encoding/json
reconhece tem uma chave de json
e um valor que controla a saída. Ao colocar a versão em camel case dos nomes dos campos como o valor para a chave json
, o codificador usará esse nome em vez disso. Este exemplo corrige as duas tentativas anteriores:
Isso irá produzir:
Output{
"name": "Sammy the Shark",
"password": "fisharegreat",
"preferredFish": null,
"createdAt": "2019-09-23T18:16:17.57739-04:00"
}
Nós mudamos os campos da struct de volta para serem visíveis para outros pacotes, capitalizando as primeiras letras de seus nomes. No entanto, desta vez, adicionamos tags de struct na forma de json:"nome"
, onde "nome"
era o nome que queríamos que o json.MarshalIndent
usasse ao imprimir nossa struct como JSON.
Agora conseguimos formatar corretamente nosso JSON. Observe, no entanto, que os campos para alguns valores foram impressos mesmo que não tenhamos definido esses valores. O codificador JSON também pode eliminar esses campos, se desejar.
Removendo Campos JSON Vazios
É comum suprimir a saída de campos não definidos em JSON. Como todos os tipos em Go têm um “valor zero”, algum valor padrão para o qual são definidos, o pacote encoding/json
precisa de informações adicionais para poder determinar que um campo deve ser considerado não definido quando assume esse valor zero. Na parte de valor de qualquer tag json
de uma struct, você pode sufixar o nome desejado do seu campo com ,omitempty
para dizer ao codificador JSON para suprimir a saída desse campo quando o campo estiver definido como o valor zero. O exemplo a seguir corrige os exemplos anteriores para não mais exibir campos vazios:
Este exemplo irá produzir:
Output{
"name": "Sammy the Shark",
"password": "fisharegreat",
"createdAt": "2019-09-23T18:21:53.863846-04:00"
}
Nós modificamos os exemplos anteriores para que o campo PreferredFish
agora tenha a tag de struct json:"preferredFish,omitempty"
. A presença da suplementação ,omitempty
faz com que o codificador JSON ignore esse campo, já que decidimos deixá-lo não definido. Isso tinha o valor null
nas saídas dos nossos exemplos anteriores.
Esta saída está parecendo muito melhor, mas ainda estamos imprimindo a senha do usuário. O pacote encoding/json
fornece outra maneira de ignorar campos privados completamente.
Ignorando Campos Privados
Alguns campos devem ser exportados de structs para que outros pacotes possam interagir corretamente com o tipo. No entanto, a natureza desses campos pode ser sensível, então, nessas circunstâncias, gostaríamos que o codificador JSON ignorasse completamente o campo — mesmo quando estiver definido. Isso é feito usando o valor especial -
como argumento de valor para uma tag de struct json:
.
Este exemplo corrige o problema de expor a senha do usuário.
Ao executar este exemplo, você verá esta saída:
Output{
"name": "Sammy the Shark",
"createdAt": "2019-09-23T16:08:21.124481-04:00"
}
A única coisa que mudamos neste exemplo em relação aos anteriores é que o campo de senha agora usa o valor especial "-"
para sua tag de struct json:
. Na saída deste exemplo, o campo password
não está mais presente.
Esses recursos do pacote encoding/json
— ,omitempty
, "-"
e outras opções — não são padrões. O que um pacote decide fazer com os valores de uma tag de struct depende de sua implementação. Como o pacote encoding/json
faz parte da biblioteca padrão, outros pacotes também implementaram esses recursos da mesma maneira como uma questão de convenção. No entanto, é importante ler a documentação de qualquer pacote de terceiros que use tags de struct para aprender o que é suportado e o que não é.
Conclusão
As tags de estrutura oferecem um meio poderoso para aumentar a funcionalidade do código que trabalha com suas estruturas. Muitas bibliotecas padrão e de terceiros oferecem maneiras de personalizar sua operação por meio do uso de tags de estrutura. Usá-las efetivamente em seu código fornece tanto esse comportamento de personalização quanto documenta sucintamente como esses campos são usados para futuros desenvolvedores.
Source:
https://www.digitalocean.com/community/tutorials/how-to-use-struct-tags-in-go