One place for hosting & domains

      Structs

      Definindo structs no Go


      Introdução

      Compilar abstrações em volta de detalhes concretos é a maior ferramenta que uma linguagem de programação pode dar a um desenvolvedor. O struct permite que os desenvolvedores do Go descrevam o mundo no qual um programa em Go opera. Em vez de pensar em strings descrevendo uma Street (Rua), City (Cidade) ou um PostalCode (CEP), os structs nos permitem falar sobre um Address (Endereço). Eles servem como uma ligação natural com a documentação em nossos esforços para dizer aos futuros desenvolvedores (incluindo nós mesmos) quais dados são importantes para nossos programas em Go e como o código futuro deve usar esses dados de maneira correta. Os structs podem ser definido e usados de diferentes maneiras. Neste tutorial, vamos dar uma olhada em cada uma dessas técnicas.

      Definindo Structs

      Os Structs funcionam como formulários de papel que você pode usar, por exemplo, para registrar seus impostos. Os formulários de papel podem ter campos para informações de texto como seu nome e sobrenome. Além dos campos de texto, os formulários podem ter caixas de seleção para indicar valores booleanos, como “casado” ou “solteiro”, ou campos de datas para a data de nascimento. De igual modo, os structs coletam diferentes dados e os organizam sob diferentes nomes de campo. Quando você inicializa uma variável com um novo struct, é como se você tivesse feito uma cópia de um formulário e o deixou pronto para preencher.

      Para criar um novo struct, você deve primeiro dar ao Go um plano gráfico que descreva os campos do struct. Essa definição do struct geralmente começa com a palavra-chave type, seguida pelo nome do struct. Após isso, utilize a palavra-chave struct, seguida de um par de chaves {}, onde você declara os campos que o struct irá conter. Assim que definir o struct, você poderá, então, declarar as variáveis que usam essa definição de struct. Este exemplo define um struct e o usa:

      package main
      
      import "fmt"
      
      type Creature struct {
          Name string
      }
      
      func main() {
          c := Creature{
              Name: "Sammy the Shark",
          }
          fmt.Println(c.Name)
      }
      

      Quando você executar esse código, verá o seguinte resultado:

      output

      Sammy the Shark

      Neste exemplo, primeiro definimos um struct Creature, contendo um campo Name do tipo string. Dentro do corpo do main, criamos uma instância de Creature, colocando um par de chaves após o nome do tipo, Creature e, em seguida, especificamos os valores para os campos daquela instância. A instância em c terá seu campo Name configurado para “Sammy the Shark”(Sammy, o tubarão). Dentro da invocação da função fmt.Println, recuperamos os valores do campo da instância, colocando um ponto após a variável em que a instância foi criada, seguida pelo nome do campo que gostaríamos de acessar. Por exemplo, o c.Name, neste caso, retorna o campo Name.

      Ao declarar uma nova instância de um struct, você geralmente enumera os nomes de campo com seus valores, como no último exemplo. De forma alternativa, se cada valor de campo for fornecido durante a instanciação de um struct, você pode omitir os nomes de campo, como neste exemplo:

      package main
      
      import "fmt"
      
      type Creature struct {
          Name string
          Type string
      }
      
      func main() {
          c := Creature{"Sammy", "Shark"}
          fmt.Println(c.Name, "the", c.Type)
      }
      

      O resultado é o mesmo que o do último exemplo:

      output

      Sammy the Shark

      Adicionamos um campo extra para a Creature rastrear o Type de criatura como uma string. Ao instanciar a Creature dentro do corpo do main, optamos pelo uso do formulário de instanciação mais curto, fornecendo valores para cada campo em ordem e omitindo seus nomes de campo. Na declaração Creature{"Sammy", "Shark"}, o campo Name recebe o valor Sammy e o campo Type recebe o valor Shark, pois o Name aparece primeiro na declaração de tipo, seguido pelo Type.

      Este formulário de declaração mais curto tem algumas desvantagens que levaram a comunidade Go a preferir o formulário mais longo, na maioria das circunstâncias. Você deve fornecer os valores de cada campo no struct ao usar a declaração curta — você não pode ignorar os campos com os quais não se importa. Isso faz com que as declarações curtas para structs com muitos campos se tornem confusas rapidamente. Por esse motivo, declarar os structs usando o formulário curto é normalmente usado com structs que têm poucos campos.

      Até agora, os nomes de campos nos exemplos começaram todos com letras maiúsculas. Isso é mais significativo do que uma preferência estilística. O uso de letras maiúsculas ou minúsculas para os nomes dos campos afeta a acessibilidade dos seus nomes de campos pelos códigos em execução em outros pacotes.

      Exportação do campo de struct

      Os campos de um struct seguem as mesmas regras de exportação que outros identificadores dentro da linguagem de programação Go. Se um nome de campo iniciar com uma letra maiúscula, ele será legível e gravável pelo código fora do pacote onde o struct foi definido. Se o campo iniciar com uma letra minúscula, apenas o código dentro do pacote do struct poderá ler e gravar naquele campo. Este exemplo define os campos que são exportados e os que não são:

      package main
      
      import "fmt"
      
      type Creature struct {
          Name string
          Type string
      
          password string
      }
      
      func main() {
          c := Creature{
              Name: "Sammy",
              Type: "Shark",
      
              password: "secret",
          }
          fmt.Println(c.Name, "the", c.Type)
          fmt.Println("Password is", c.password)
      }
      

      Isso resultará em:

      output

      Sammy the Shark Password is secret

      Acrescentamos um campo adicional aos nossos exemplos anteriores, secret.secret, que é um campo de string não exportado, o que significa que qualquer outro pacote que tentar instanciar uma Creature não poderá acessar ou definir seu campo secret. Dentro do mesmo pacote, podemos acessar esses campos, como esse exemplo fez. Como o main também está no pacote main, ele pode referir o c.password e recuperar o valor armazenado lá. É comum ter campos não exportados em structs com acesso a eles e mediados por métodos exportados.

      Structs em linha

      Além de definir um novo tipo para representar um struct, você também pode definir um struct em linha. Essas definições de struct dinâmicas são úteis em situações onde a criação de novos nomes para tipos de struct seriam um esforço perdido. Por exemplo, com frequência os testes usam um struct para definir todos os parâmetros que compõem um caso de teste em particular. Seria complicado inventar novos nomes como CreatureNamePrintingTestCase quando esse struct é usado em apenas um lugar.

      As definições de struct em linha aparecem à direita de uma atribuição de variável. Você deve fornecer uma instanciação deles imediatamente após, fornecendo um par de chaves adicional com os valores de cada um dos campos que você definir. O exemplo seguinte mostra uma definição de um struct em linha:

      package main
      
      import "fmt"
      
      func main() {
          c := struct {
              Name string
              Type string
          }{
              Name: "Sammy",
              Type: "Shark",
          }
          fmt.Println(c.Name, "the", c.Type)
      }
      

      O resultado deste exemplo será:

      output

      Sammy the Shark

      Em vez de definir um novo tipo descrevendo nosso struct com a palavra-chave type, esse exemplo define um struct em linha colocando a definição do struct imediatamente após o operador de atribuição curto, :=. Definimos os campos do struct como nos exemplos anteriores, mas devemos fornecer imediatamente outro par de chaves e os valores que cada campo irá assumir. Agora, o uso desse struct ficou exatamente como era antes — podemo-nos referir aos nomes de campo usando a notação de ponto. O lugar mais comum em que você verá os structs declarados em linha será durante testes, já que os structs únicos são definidos para conter dados e expectativas de um caso de teste em particular.

      Conclusão

      Os structs são coleções de dados heterogêneos, definidos pelo programadores para organizar informações. A maioria dos programas lida com volumes de dados imensos e, sem structs, seria difícil lembrar quais variáveis de string ou int pertenciam juntas ou quais eram diferentes. Da próxima vez que você se encontrar manipulando grupos de variáveis, pergunte a si mesmo se essas variáveis ficariam melhor agrupadas usando um struct. Essas variáveis podem estar descrevendo um conceito de nível superior desde o começo.



      Source link

      Definir structs en Go


      Introducción

      La capacidad para crear abstracciones en torno a detalles concretos es la mejor herramienta que un lenguaje de programación puede ofrecer a un desarrollador. Las “structs” permiten que los desarrolladores de Go describan el mundo en el que un programa de Go funciona. En vez de razonar sobre cadenas que describen una Street, una City o un PostalCode, las structs nos permiten hablar sobre una Address. Sirven como un nexo natural para la documentación en nuestros esfuerzos por indicar a desarrolladores futuros (incluidos nosotros mismos) los datos que son importantes para nuestros programas de Go y la forma en que el código futuro debería usar esos datos como corresponde. Las structs pueden definirse de varias formas diferentes. En este tutorial, veremos cada una de estas técnicas.

      Definir structs

      Las structs funcionan como formularios en papel que podría usar, por ejemplo, para declarar sus impuestos. Los formularios en papel tienen campos para datos textuales, como su nombre y apellido. A parte de los campos de texto, los formularios pueden tener casillas de verificación para indicar valores booleanos como “married” (casado) o “single” (soltero), o campos de fecha para la fecha de nacimiento. De forma similar, las structs recogen diferentes datos y los organizan con diferentes nombres de campos. Cuando inicia una variable con una nueva struct, es como si fotocopiase un formulario y lo dejase listo para completarse.

      Para crear una nueva struct, primero debe proporcionar a Go un esquema que describa los campos que esta contiene. Esta definición de struct normalmente comienza con la palabra clave type seguida por el nombre de la struct. Después de esto, utilice la palabra clave struct seguida de un par de llaves {} en el que declare los campos que tendrá la struct. Una vez que defina la struct, podrá declarar las variables que usan esta definición de struct. En este ejemplo se define y se utiliza una struct:

      package main
      
      import "fmt"
      
      type Creature struct {
          Name string
      }
      
      func main() {
          c := Creature{
              Name: "Sammy the Shark",
          }
          fmt.Println(c.Name)
      }
      

      Cuando ejecute este código, verá este resultado:

      output

      Sammy the Shark

      Primero definimos una struct Creature en este ejemplo, la cual contiene un campo Name de tipo string. En el cuerpo de main, creamos una instancia de Creature colocando un par de corchetes después del nombre del tipo, Creature, y luego especificando los valores para los campos de esa instancia. El campo Name de la instancia c contendrá “Sammy the Shark”. En la invocación de la función fmt.PrintIn, obtenemos los valores del campo de instancia ubicando un punto después de la variable en la que se creó la instancia y, después de esto, el nombre del campo al que queremos acceder. Por ejemplo, c.Name en este caso muestra el campo Name.

      Cuando se declara una nueva instancia de una struct, generalmente se enumeran los nombres de campos con sus valores, como en el último ejemplo. También se pueden omitir los nombres de campos, si el valor de cada campo se proporciona durante la creación de instancias de una struct, como en este ejemplo:

      package main
      
      import "fmt"
      
      type Creature struct {
          Name string
          Type string
      }
      
      func main() {
          c := Creature{"Sammy", "Shark"}
          fmt.Println(c.Name, "the", c.Type)
      }
      

      El resultado es el mismo que el del último ejemplo:

      output

      Sammy the Shark

      Agregamos un campo a Creature para realizar un seguimiento del Type de criatura como una string. Al crear una instancia de Creature en el cuerpo de main, optamos por usar la forma de creación de instancias más corta proporcionando valores para cada campo a fin de ordenar y omitir los nombres de estos. En la declaración Creature{"Sammy", "Shark"}, el campo Name toma el valor Sammy y el campo Type toma el valor Shark porque Name aparece primero en la declaración de tipo, seguido de Type.

      Esta forma de declaración más corta tiene algunas desventajas que hicieron que la comunidad Go prefiriera la forma más larga en la mayoría de las circunstancias. Debe proporcionar valores para cada campo en la struct al utilizar la declaración corta; no puede omitir los campos que no le interesan. Esto rápidamente hace que las declaraciones cortas para las structs con muchos campos se vuelvan confusas. Por este motivo, declarar structs usando la forma corta es común con structs que tienen pocos campos.

      Los nombres de campo de los ejemplos hasta el momento comenzaron con letras mayúsculas. Esto es más importante que una preferencia estilística. El uso de letras mayúsculas o minúsculas para nombres de campos determina si el código que se ejecute en otros paquetes podrá acceder a ellos.

      Exportación de campos de struct

      Los campos de una struct siguen las mismas reglas de exportación que otros identificadores del lenguaje de programación Go. Si un nombre de campo comienza con una letra mayúscula, será legible y se podrá escribir a través de código que se encuentre fuera del paquete en el que se definió la struct. Si el campo comienza con una letra minúscula, solo el código dentro del paquete de esa struct podrá realizar tareas de lectura y escritura en ese campo. En este ejemplo definen los campos que se exportan y los que no:

      package main
      
      import "fmt"
      
      type Creature struct {
          Name string
          Type string
      
          password string
      }
      
      func main() {
          c := Creature{
              Name: "Sammy",
              Type: "Shark",
      
              password: "secret",
          }
          fmt.Println(c.Name, "the", c.Type)
          fmt.Println("Password is", c.password)
      }
      

      El resultado será el siguiente:

      output

      Sammy the Shark Password is secret

      Agregamos un campo a nuestros ejemplos anteriores: secret. secret es una string no exportada, lo cual significa que cualquier otro paquete que intente crear una instancia de una Creature no podrá acceder a su campo secret ni configurarlo. En el mismo paquete, podemos acceder a estos campos, como en este ejemplo. Ya que main está también en el paquete main, puede hacer referencia a c.password y obtener el valor almacenado allí. Es común que haya campos no exportado en structs con acceso a ellos mediado por los métodos exportados.

      Structs en línea

      Además de definir un nuevo tipo para representar una struct, puede definir una struct en línea. Estas definiciones de structs sobre la marcha son útiles en situaciones en las cuales inventar nuevos nombres para los tipos de structs sería un esfuerzo inútil. Por ejemplo, en las pruebas a menudo se utilizan un struct para definir todos los parámetros que forman un caso de prueba concreto. Sería engorroso pensar en nuevos nombres como CreatureNamePrintingTestCase cuando esa struct se utiliza en un único lugar.

      Las definiciones de structs en línea aparecen en el lado derecho de una asignación de variable. Debe proporcionar una creación de instancias de ellas de inmediato proveyendo un par adicional de corchetes con valores para cada uno de los campos que defina. En el siguiente ejemplo se muestra la definición de una struct en línea:

      package main
      
      import "fmt"
      
      func main() {
          c := struct {
              Name string
              Type string
          }{
              Name: "Sammy",
              Type: "Shark",
          }
          fmt.Println(c.Name, "the", c.Type)
      }
      

      El resultado de este ejemplo será el siguiente:

      output

      Sammy the Shark

      En vez de definir un nuevo tipo que describa nuestra struct con la palabra clave type, este ejemplo define una struct en línea disponiendo la definición de struct inmediatamente después el operador de asignación corta :=. Definimos los campos de la struct como en los ejemplos anteriores, pero luego debemos proporcionar de inmediato otro par de llaves y los valores que cada campo asumirá. Esta struct ahora se usa exactamente de la misma manera que antes; podemos hacer referencia a los nombres de campos usando la notación de puntos. Verá structs en línea declaradas con mayor frecuencia durante pruebas, ya que a menudo las structs puntuales se definen para contener datos y expectativas para un caso de prueba concreto.

      Conclusión

      Las structs son colecciones de datos heterogéneos definidas por los programadores para organizar la información. La mayoría de los programas manejan enormes volúmenes de datos, y sin structs sería difícil recordar las variables string o int que debían estar juntas o las que eran diferentes. La próxima vez que se encuentre haciendo malabares con grupos de variables, pregúntese si quizá esas variables estarían mejor agrupadas con una struct. Es posible que esas variables hayan descrito un concepto de nivel más alto todo el tiempo.



      Source link

      Cómo usar etiquetas de structs en Go


      Introducción

      Las estructuras, o structs, se utilizan para recopilar varios datos en una unidad. Estas recopilaciones de información se utilizan para describir conceptos de nivel más alto, como un valor Address compuesto de parámetros Street, City, State y PostalCode. Cuando lee esta información desde sistemas como bases de datos o API, pude usar las etiquetas “struct” para controlar la manera en que se asigna esta información a los campos de una struct. Las etiquetas de structs son pequeños fragmentos de metadatos adjuntos a campos de una struct que proporcionan instrucciones a otro código de Go que funciona con las structs.

      ¿Qué aspecto tiene una etiqueta de struct?

      Las etiquetas de struct de Go son anotaciones que aparecen después del tipo en una declaración de struct de Go. Cada etiqueta se compone de cadenas cortas asociadas con algún valor correspondiente.

      Una etiqueta de struct tiene este aspecto y se cierra cerrada con caracteres de acento grave `.

      type User struct {
          Name string `example:"name"`
      }
      

      Otro código de Go será capaz de examinar estas structs y extraer los valores asignados a claves específicas que solicita. Las etiquetas de structs no afectan al funcionamiento de su código sin no hay otro código que las examine.

      Pruebe este ejemplo para ver el aspecto de las etiquetas de structs y que, sin código de otro paquete, no tendrán ningún efecto.

      package main
      
      import "fmt"
      
      type User struct {
          Name string `example:"name"`
      }
      
      func (u *User) String() string {
          return fmt.Sprintf("Hi! My name is %s", u.Name)
      }
      
      func main() {
          u := &User{
              Name: "Sammy",
          }
      
          fmt.Println(u)
      }
      

      El resultado será el siguiente:

      Output

      Hi! My name is Sammy

      En este ejemplo se define un tipo de User con un campo Name. El campo Name tiene la etiqueta de struct example:"name". En una conversación, nos referiríamos a esta etiqueta específica en la conversación como la “etiqueta de struct de ejemplo” porque en ella se utiliza la palabra “example” como clave. La etiqueta de struct example tiene el valor "name" para el campo Name. En el tipo User, también definimos el método String() requerido por la interfaz fmt.Stringer. Esto se invocará automáticamente cuando pasemos el tipo a fmt.PrintIn y nos dará la oportunidad de producir una versión con buen formato de nuestra struct.

      En el cuerpo de main, creamos una nueva instancia de nuestro tipo User y la pasamos a fmt.PrintIn. Aunque la struct tenía una etiqueta de struct presente, vemos que no afecta al funcionamiento de este código de Go. Se comportará exactamente igual que si la etiqueta de struct no estuviese presente.

      Para usar las etiquetas de struct a fin de conseguir algo, se debe escribir otro código de Go para examinar las structs en el tiempo de ejecución. La biblioteca estándar tiene paquetes que utilizan las etiquetas de structs como parte de su funcionamiento. El más popular es encoding/json.

      Encoding JSON

      La notación de objetos JavaScript (JSON) es un formato textual pensado para codificar conjuntos de datos organizados bajo diferentes claves de cadena. Normalmente, se utiliza para comunicar datos entre diferentes programas, ya que el formato es suficientemente sencillo como para que existan bibliotecas para decodificarlo en muchos lenguajes diferentes. El siguiente es un ejemplo de JSON:

      {
        "language": "Go",
        "mascot": "Gopher"
      }
      

      Este objeto JSON contiene dos claves: language y mascot. Después de estas claves se encuentran los valores asociados. Aquí, la clave language es un valor de Go y mascot tiene asignado el valor Gopher.

      El codificador JSON de la biblioteca estándar utiliza etiquetas de struct como anotaciones que indican a este la forma en que desearía nombrar sus campos en el resultado de JSON. Estos mecanismos de codificación y decodificación de JSON pueden encontrarse en el paquete encoding/json.

      Pruebe este ejemplo para ver la forma en que JSON se codifica sin etiquetas de struct:

      package main
      
      import (
          "encoding/json"
          "fmt"
          "log"
          "os"
          "time"
      )
      
      type User struct {
          Name          string
          Password      string
          PreferredFish []string
          CreatedAt     time.Time
      }
      
      func main() {
          u := &User{
              Name:      "Sammy the Shark",
              Password:  "fisharegreat",
              CreatedAt: time.Now(),
          }
      
          out, err := json.MarshalIndent(u, "", "  ")
          if err != nil {
              log.Println(err)
              os.Exit(1)
          }
      
          fmt.Println(string(out))
      }
      

      Esto imprimirá el siguiente resultado:

      Output

      { "Name": "Sammy the Shark", "Password": "fisharegreat", "CreatedAt": "2019-09-23T15:50:01.203059-04:00" }

      Definimos una struct que describe un usuario con campos que incluyen su nombre, su contraseña y el momento de creación del usuario. En la función main, creamos una instancia de este usuario suministrando valores para todos los campos a excepción de PreferredFish (a Sammy le gusta todo el pescado). Luego pasamos la instancia de User a la función json.MarshalIndent. Esto se utiliza para que podamos ver con mayor facilidad el resultado de JSON sin usar una herramienta de formato externa. Esta invocación podría sustituirse por json.Marshal(u) para recibir JSON sin ningún espacio en blanco adicional. Los dos argumentos adicionales para json.MarshalIndent controlan el prefijo del resultado (que omitimos con la cadena vacía) y los caracteres que se usarán para hacer sangría, que aquí son dos caracteres de espacio. Cualquier error producido a partir de json.MarshalIndent se registra y el programa se cierra usando os.Exit(1). Finalmente, emitimos el []byte devuelto de json.MarshalIndent hacia una string y entregamos la cadena resultante a fmt.PrintIn para su impresión en el terminal.

      Los campos de la struct aparecen exactamente como los nombramos. Este no es el estilo JSON típico que podría esperar, que utiliza CamelCase para los nombres de los campos. Podrá cambiar los nombres del campo para que sigan la capitalización CamelCase en este ejemplo siguiente: Como verá cuando ejecute este ejemplo, esto no funcionará porque los nombres deseados del campo entran en conflicto con las reglas de Go sobre nombres de campo exportados.

      package main
      
      import (
          "encoding/json"
          "fmt"
          "log"
          "os"
          "time"
      )
      
      type User struct {
          name          string
          password      string
          preferredFish []string
          createdAt     time.Time
      }
      
      func main() {
          u := &User{
              name:      "Sammy the Shark",
              password:  "fisharegreat",
              createdAt: time.Now(),
          }
      
          out, err := json.MarshalIndent(u, "", "  ")
          if err != nil {
              log.Println(err)
              os.Exit(1)
          }
      
          fmt.Println(string(out))
      }
      

      Con esto, se presentará el siguiente resultado:

      Output

      {}

      En esta versión, alteramos los nombres de los campos para que tengan estilo CamelCase. Ahora Name es name, Password es password y, por último, CreatedAt es createdAt. En el cuerpo de main, cambiamos la creación de instancia de nuestra struct para que use estos nuevos nombres. Luego pasamos la struct a la función json.MarshlIndent como antes. El resultado, esta vez es un objeto JSON vacío, {}.

      Los campos con estilo CamelCase requieren que el primer carácter esté en minúscula. Aunque en JSON no se tiene en cuenta la forma en que usted asigna nombres a sus campos, en Go sí se considera, ya que indica la visibilidad del campo fuera del paquete. Debido a que el paquete encoding/json es independiente del paquete main que usamos, debemos poner en mayúscula el primer carácter a fin de que sea visible para encoding/json. Parecería que nos hallamos en un impasse y necesitamos alguna forma de notificar al codificador de JSON que nos gustaría nombrar este campo.

      Usar etiquetas de struct para controlar la codificación

      Puede modificar el ejemplo anterior para que tenga campos exportados que estén codificados correctamente con nombres de campos con estilo CamelCase realizando en cada campo anotaciones con una etiqueta de struct. La etiqueta de struct que encoding/json reconoce tiene una clave de json y un valor que controla el resultado. Cuando se disponga la versión en estilo CamelCase de los nombres del campo como valor para la clave json, el codificador usará ese nombre como alternativa. En este ejemplo, se corrigen los dos intentos anteriores:

      package main
      
      import (
          "encoding/json"
          "fmt"
          "log"
          "os"
          "time"
      )
      
      type User struct {
          Name          string    `json:"name"`
          Password      string    `json:"password"`
          PreferredFish []string  `json:"preferredFish"`
          CreatedAt     time.Time `json:"createdAt"`
      }
      
      func main() {
          u := &User{
              Name:      "Sammy the Shark",
              Password:  "fisharegreat",
              CreatedAt: time.Now(),
          }
      
          out, err := json.MarshalIndent(u, "", "  ")
          if err != nil {
              log.Println(err)
              os.Exit(1)
          }
      
          fmt.Println(string(out))
      }
      

      El resultado será el siguiente:

      Output

      { "name": "Sammy the Shark", "password": "fisharegreat", "preferredFish": null, "createdAt": "2019-09-23T18:16:17.57739-04:00" }

      Cambiamos los nombres de campos para que sean visibles para otros paquetes disponiendo en mayúsculas las primeras letras de sus nombres. Sin embargo, esta vez añadimos etiquetas de struct en el formato de json:"name", donde "name" era el nombre que queríamos que json.MarshalIndent usarara al imprimir nuestra struct como JSON.

      De esta manera, configuramos nuestro JSON correctamente. Tenga en cuenta, sin embargo, que los campos para algunos valores se imprimieron aunque no establecimos dichos valores. El codificador también JSON puede eliminar estos campos, si lo desea.

      Eliminar campos JSON vacíos

      Normalmente, nos conviene suprimir campos de salida que no están establecidos en JSON. Debido a que todos los tipos de Go tienen un “valor cero”, algún valor predeterminado en el que se fijan, el paquete encoding/json necesita información adicional para poder indicar que algún campo debería considerarse como no establecido cuando asume este valor cero. En la parte del valor de cualquier etiqueta de struct json, puede añadir un sufijo al nombre deseado de su campo con ,omitempty para indicar al codificador JSON que suprima el resultado de este campo cuando se fije en el valor cero. En el siguiente ejemplo, se corrigen los ejemplos anteriores para que no produzcan campos vacíos:

      package main
      
      import (
          "encoding/json"
          "fmt"
          "log"
          "os"
          "time"
      )
      
      type User struct {
          Name          string    `json:"name"`
          Password      string    `json:"password"`
          PreferredFish []string  `json:"preferredFish,omitempty"`
          CreatedAt     time.Time `json:"createdAt"`
      }
      
      func main() {
          u := &User{
              Name:      "Sammy the Shark",
              Password:  "fisharegreat",
              CreatedAt: time.Now(),
          }
      
          out, err := json.MarshalIndent(u, "", "  ")
          if err != nil {
              log.Println(err)
              os.Exit(1)
          }
      
          fmt.Println(string(out))
      }
      

      El resultado de este ejemplo será el siguiente:

      Output

      { "name": "Sammy the Shark", "password": "fisharegreat", "createdAt": "2019-09-23T18:21:53.863846-04:00" }

      Modificamos los ejemplos anteriores para que el campo PreferredFish ahora tenga la etiqueta de struct json:"preferredFish,omitempty". La presencia del aumento ,omitempty hace que el codificador de JSON omita ese campo, ya que decidimos dejar su configuración indefinida. Esto tenía el valor null en los resultados de nuestros ejemplos anteriores.

      Este resultado es mucho mejor, pero aún se imprime la contraseña del usuario. El paquete encoding/json ofrece otra alternativa que nos permite ignorar los campos privados por completo.

      Ignorar campos privados

      Algunos campos deben exportarse desde las structs para que otros paquetes puedan interactuar correctamente con el tipo. Sin embargo, la naturaleza de esos campos puede ser sensible, de modo que en estas circunstancias nos gustaría que el codificador de JSON ignorase el campo por completo, incluso cuando esté establecido. Esto se hace usando el valor especial - como argumento del valor para una etiqueta de struct json:.

      En este ejemplo se corrige el problema de la exposición de la contraseña del usuario.

      package main
      
      import (
          "encoding/json"
          "fmt"
          "log"
          "os"
          "time"
      )
      
      type User struct {
          Name      string    `json:"name"`
          Password  string    `json:"-"`
          CreatedAt time.Time `json:"createdAt"`
      }
      
      func main() {
          u := &User{
              Name:      "Sammy the Shark",
              Password:  "fisharegreat",
              CreatedAt: time.Now(),
          }
      
          out, err := json.MarshalIndent(u, "", "  ")
          if err != nil {
              log.Println(err)
              os.Exit(1)
          }
      
          fmt.Println(string(out))
      }
      

      Cuando ejecute este ejemplo, verá este resultado:

      Output

      { "name": "Sammy the Shark", "createdAt": "2019-09-23T16:08:21.124481-04:00" }

      Lo único que cambiamos en este ejemplo respecto de los anteriores es que el campo de contraseña ahora utiliza el valor especial "-“ para su etiqueta de struct json:. Vemos que en el resultado de este ejemplo, el campo contraseña ya no está presente.

      Estas características del paquete encoding/json, ,omitempty y "-" no son estándares. La acción que se aplique a través de un paquete en los valores de una etiqueta de struct depende de su implementación. Debido a que el paquete encoding/jason es parte de la biblioteca estándar, otros paquetes implementaron también estas funciones de la misma forma por convención. Sin embargo, es importante leer la documentación de cualquier paquete de terceros que utilice etiquetas de struct para conocer lo que es y no es compatible.

      Conclusión

      Las etiquetas de struct ofrecen una alternativa poderosa para aumentar la funcionalidad del código que funciona con sus structs. Muchos paquetes de bibliotecas estándares y de terceros ofrecen formas de personalizar su operación a través de etiquetas de structs. El uso efectivo de estas en su código proporciona este comportamiento de personalización y documenta de forma sucinta la forma en que estos campos se usan para futuros desarrolladores.



      Source link