One place for hosting & domains

      etiquetas

      Personalizar los binarios de Go con etiquetas de compilación


      Introducción

      En Go, una etiqueta de compilación, o una restricción de compilación, es un identificador añadido a un fragmento de código que determina el momento en que debería incluirse el archivo en un paquete durante el proceso build. Esto le permite compilar diferentes versiones de su aplicación de Go a partir del mismo código fuente y alternar entre ellas de forma rápida y organizada. Muchos desarrolladores utilizan etiquetas de compilación para mejorar el flujo de trabajo de creación de aplicaciones compatibles entre plataformas, como programas que requieren cambios de código para tener en cuenta las variaciones entre diferentes sistemas operativos. Las etiquetas de compilación también se utilizan para pruebas de integración, lo que le permite pasar rápidamente del código integrado al código con un servicio mock o stub, y para diferentes niveles de conjuntos de funciones en una aplicación.

      Tomaremos como ejemplo el problema de los diferentes conjuntos de funciones de los clientes. Al escribir algunas aplicaciones, es posible que desee controlar las funciones que se incluirán en el binario, como en el caso de una aplicación que ofrece niveles Gratis, Pro y Empresarial. A medida que el cliente aumente su nivel de suscripción en esas aplicaciones, más funciones se desbloquearán y estarán disponibles. Para resolver este problema, podría tener proyectos separados e intentar mantenerlos sincronizados entre ellos a través de instrucciones import. Aunque este enfoque funcionaría, con el tiempo se volvería tedioso y estaría expuesto a errores. Un enfoque alternativo consistiría en usar etiquetas de compilación.

      A través de este artículo, usará etiquetas de compilación en Go para generar binarios ejecutables diferentes que ofrezcan conjuntos de funciones Gratis, Pro y Empresarial de una aplicación de ejemplo. Cada uno tendrá un conjunto diferente de funciones disponibles y la versión Gratis será la predeterminada.

      Requisitos previos

      Para seguir el ejemplo de este artículo, necesitará lo siguiente:

      Compilar la versión Gratis

      Comenzaremos creando la versión Gratis de la aplicación, ya que será la predeterminada cuando se ejecute go build sin etiquetas de compilación. Más adelante, usaremos las etiquetas de compilación para añadir otras partes a nuestro programa.

      En el directorio src, cree una carpeta con el nombre de su aplicación. En este tutorial se usará app:

      Posiciónese en esta carpeta:

      A continuación, cree en su editor de texto un nuevo archivo de texto llamado main.go:

      A continuación, definiremos la versión Gratis de la aplicación. Añada el siguiente contenido a main.go:

      main.go

      package main
      
      import "fmt"
      
      var features = []string{
        "Free Feature #1",
        "Free Feature #2",
      }
      
      func main() {
        for _, f := range features {
          fmt.Println(">", f)
        }
      }
      

      En este archivo, creamos un programa que declara un segmento llamado features, el cual contiene dos cadenas que representan las funciones de nuestra aplicación Gratis. La función main() de la aplicación utiliza un bucle for para aplicar range en el segmento features e imprimir todas las funciones disponibles en la pantalla.

      Guarde el archivo y ciérrelo. Ahora que se guardó el archivo, ya no tendremos que editarlo durante el resto del artículo. Como alternativa, utilizaremos etiquetas de compilación para cambiar las funciones de los binarios que crearemos a partir de él.

      Cree y ejecute el programa:

      Obtendrá el siguiente resultado:

      Output

      > Free Feature #1 > Free Feature #2

      El programa imprimió nuestras dos funciones gratuitas y completó la versión Gratis de nuestra aplicación.

      Hasta ahora, creó una aplicación que tiene un conjunto de funciones muy básico. A continuación, compilará una alternativa para añadir más funciones a la aplicación en el tiempo de compilación.

      Añadir las funciones Pro con go build

      Hasta ahora, evitamos realizar cambios en main.go y simulamos un entorno de producción común en el cual se debe agregar código sin cambiar ni posiblemente dañar el código principal. Debido a que no podemos editar el archivo main.go, tendremos que usar otro mecanismo para inyectar más funciones en el segmento features usando etiquetas de compilación.

      Vamos a crear un nuevo archivo llamado pro.go que usará una función init() para añadir más funciones al segmento features:

      Una vez que el editor haya abierto el archivo, añada las siguientes líneas:

      pro.go

      package main
      
      func init() {
        features = append(features,
          "Pro Feature #1",
          "Pro Feature #2",
        )
      }
      

      En este código, usamos init() para ejecutar código antes de la función main() de nuestra aplicación, seguido de append() para añadir las funciones Pro al segmento features. Guarde el archivo y ciérrelo.

      Compile y ejecute la aplicación usando go build:

      Debido a que ahora hay dos archivos en nuestro directorio actual (pro.go y main.go), go build creará un binario a partir de ambos. Ejecute este binario:

      Esto le proporcionará el siguiente conjunto de funciones:

      Output

      > Free Feature #1 > Free Feature #2 > Pro Feature #1 > Pro Feature #2

      En la aplicación ahora se incluirán las funciones Pro y Gratis. Sin embargo, esto no es conveniente. Debido a que no existe una distinción entre las versiones, la Gratis ahora incluye las funciones que, según se supone, solo deben estar disponibles en la versión Pro. Para solucionar esto, podría incluir más código para administrar los diferentes niveles de la aplicación o podría usar etiquetas de compilación para indicar a la cadena de herramientas de Go los archivos .go que desea crear e ignorar. Añadiremos etiquetas de compilación en el siguiente paso.

      Agregar etiquetas de compilación

      Ahora puede usar las etiquetas de compilación para distinguir la versión Pro y la Gratis de su aplicación.

      Comenzaremos examinando el aspecto que tiene una etiqueta de compilación:

      // +build tag_name
      

      Al disponer esta línea de código como la primera de su paquete y sustituir tag_name por el nombre de su etiqueta de compilación, etiquetará este paquete como un código que puede incluirse selectivamente en el binario final. Veremos esto en acción añadiendo una etiqueta de compilación al archivo pro.go para indicar al comando go build que lo ignore a menos que se especifique la etiqueta. Abra el archivo en su editor de texto:

      A continuación, añada la siguiente línea resaltada:

      pro.go

      // +build pro
      
      package main
      
      func init() {
        features = append(features,
          "Pro Feature #1",
          "Pro Feature #2",
        )
      }
      

      En la parte superior del archivo pro.go, añadimos // +build pro seguido de una nueva línea en blanco. Esta nueva línea final es necesaria; de lo contrario, Go interpreta esto como un comentario. En la parte superior de un archivo .go también declaraciones de etiquetas de compilación. No puede haber nada, ni siquiera comentarios, sobre las etiquetas de compilación.

      La declaración +build indica al comando go build que esto no es un comentario, sino una etiqueta de compilación. La segunda parte es la etiqueta pro. Al añadirse esta etiqueta a la parte superior del archivo pro.go, el comando go build ahora incluirá únicamente el archivo pro.go cuando la etiqueta pro esté presente.

      Compile y ejecute la aplicación de nuevo:

      Obtendrá el siguiente resultado:

      Output

      > Free Feature #1 > Free Feature #2

      Debido a que requiere que haya una etiqueta pro presente, el archivo pro.go se ignora y la aplicación realiza la compilación sin él.

      Cuando se ejecuta el comando go build, podemos usar el indicador -tags para incluir condicionalmente código en la fuente compilada añadiendo la etiqueta como argumento. Haremos esto para la etiqueta pro:

      Esto dará el siguiente resultado:

      Output

      > Free Feature #1 > Free Feature #2 > Pro Feature #1 > Pro Feature #2

      Ahora, solo obtendremos las funciones adicionales cuando compilemos la aplicación usando la etiqueta de compilación pro.

      Esto está bien si solo hay dos versiones, pero la situación se complica cuando añade más etiquetas. Para añadir la versión Empresarial de nuestra aplicación en el siguiente paso, utilizaremos varias etiquetas de compilación unidas con lógica booleana.

      Lógica de booleanos para etiquetas de compilación

      Cuando hay varias etiquetas de compilación en un paquete de Go, las etiquetas interactúan entre ellas usando lógica de booleanos. Para demostrar esto, añadiremos el nivel Empresarial de nuestra aplicación usando las etiquetas pro y enterprise.

      Para crear un binario de Empresarial, debemos incluir las funciones predeterminadas, las funciones del nivel Pro y un nuevo conjunto de funciones para Empresarial. Primero, abra un editor y cree un nuevo archivo, enterprise.go, que añadirá las nuevas funciones Empresarial:

      El contenido de enterprise.go tendrá un aspecto casi idéntico a pro.go, pero contendrá nuevas funciones. Añada las líneas siguientes al archivo:

      enterprise.go

      package main
      
      func init() {
        features = append(features,
          "Enterprise Feature #1",
          "Enterprise Feature #2",
        )
      }
      

      Guarde el archivo y ciérrelo.

      Actualmente, el archivo enterprise.go no tiene ninguna etiqueta de compilación y, tal como aprendió cuando añadió pro.go, esto significa que estas funciones se añadirán a la versión Gratis cuando se ejecuta go.build. Para pro.go, agregó // +build pro y una nueva línea en la parte superior del archivo a fin de indicar a go build que solo debería incluirse cuando se utilice -tags pro. En esta situación, solo necesitó una etiqueta de compilación para lograr el objetivo. Cuando se añaden las nuevas funciones Empresarial, sin embargo, primero debe también disponer de las funciones Pro.

      Añadiremos primero compatibilidad con la etiqueta de compilación pro a enterprise.go. Abra el archivo con su editor de texto:

      A continuación, añada la etiqueta de compilación antes de la declaración package main y asegúrese de incluir una línea nueva después de la etiqueta de compilación:

      enterprise.go

      // +build pro
      
      package main
      
      func init() {
        features = append(features,
          "Enterprise Feature #1",
          "Enterprise Feature #2",
        )
      }
      

      Guarde el archivo y ciérrelo.

      Compile y ejecute la aplicación sin etiquetas:

      Obtendrá el siguiente resultado:

      Output

      > Free Feature #1 > Free Feature #2

      Las funciones Empresariales ya no se muestran en la versión Gratis. Ahora, añadiremos la etiqueta de compilación pro, y compilaremos y ejecutaremos la aplicación de nuevo:

      Obtendrá el siguiente resultado:

      Output

      > Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2 > Pro Feature #1 > Pro Feature #2

      Esto no es exactamente lo que necesitamos: las funciones Empresarial ahora se muestran cuando intentamos compilar la versión Pro. Para resolver esto, debemos usar otra etiqueta de compilación. A diferencia de lo que sucede con la etiqueta pro, sin embargo, debemos asegurarnos de que las funciones pro y enterprise estén disponibles.

      El sistema de compilación de Go tiene en cuenta esta situación al permitir el uso de lógica de booleanos básica en el sistema de etiquetas de compilación.

      Abriremos de nuevo enterprise.go:

      Añada otra etiqueta de compilación, enterprise, en la misma línea de la etiqueta pro:

      enterprise.go

      // +build pro enterprise
      
      package main
      
      func init() {
        features = append(features,
          "Enterprise Feature #1",
          "Enterprise Feature #2",
        )
      }
      

      Guarde y cierre el archivo.

      Ahora, compilaremos y ejecutaremos la aplicación con la nueva etiqueta de compilación enterprise.

      • go build -tags enterprise
      • ./app

      Esto dará el siguiente resultado:

      Output

      > Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2

      Con esto, habremos perdido las funciones Pro. Esto se debe a que cuando disponemos varias etiquetas de compilación en la misma línea en un archivo .go, go build interpreta que usan la lógica OR. Con la adición de la línea // +build pro enterprise, el archivo enterprise.go se compilará si cualquiera de las etiquetas de compilación pro o enterprise están presentes. Debemos configurar las etiquetas de compilación correctamente para requerir ambas y usar, por el contrario, la lógica AND.

      Si en vez de disponer ambas etiquetas en la misma línea las situamos en líneas separadas, go build interpretará esas etiquetas usando la lógica AND.

      Abra enterprise.go de nuevo. Separaremos las etiquetas de compilación en varias líneas.

      enterprise.go

      // +build pro
      // +build enterprise
      
      package main
      
      func init() {
        features = append(features,
          "Enterprise Feature #1",
          "Enterprise Feature #2",
        )
      }
      

      Ahora, compile y ejecute la aplicación con la nueva etiqueta de compilación enterprise.

      • go build -tags enterprise
      • ./app

      Obtendrá el siguiente resultado:

      Output

      > Free Feature #1 > Free Feature #2

      Aun no terminamos: debido a que una instrucción AND requiere que ambos elementos se consideren como true, debemos usar las etiquetas de compilación pro y enterprise.

      Haremos un nuevo intento:

      • go build -tags "enterprise pro"
      • ./app

      Obtendrá el siguiente resultado:

      Output

      > Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2 > Pro Feature #1 > Pro Feature #2

      Ahora, nuestra aplicación puede crearse a partir del mismo árbol de origen de varias formas desbloqueando las funciones de la aplicación de manera correspondiente.

      En este ejemplo, usanos una nueva etiqueta // +build para representar la lógica AND, pero existen formas alternativas de representar la lógica de booleanos con etiquetas de compilación. La siguiente tabla contiene algunos ejemplos de otros formatos sintácticos para etiquetas de compilación, junto con su equivalente booleano:

      Sintaxis de la etiqueta de compilación Ejemplo de etiqueta de compilación Instrucción booleana
      Elementos separados por espacios // +build pro enterprise pro OR enterprise
      Elementos separados por comas // +build pro enterprise pro AND enterprise
      Elementos con signo de exclamación // + build ! pro NOT pro

      Conclusión

      A lo largo de este tutorial, utilizó etiquetas de compilación para controlar la parte de su código que se compilaría en el binario. Primero, declaró las etiquetas de compilación y las utilizó con go build, y luego combinó varias etiquetas con lógica de booleanos. Luego creó un programa que representó los tres conjuntos diferentes de funciones de una versión Gratis, Pro y Empresarial, y mostró el potente nivel de control que las etiquetas de compilación pueden proporcionarle en sus proyectos.

      Si desea obtener más información sobre las etiquetas de compilación, consulte la documentación de Golang relacionada con este tema o siga explorando nuestra serie Cómo crear código en Go.



      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