Esto comenzó como una simple publicación en un grupo interno de Slack. Luego me pidieron que lo publicara en otro canal de ese Slack. Luego decidí publicarlo en DevRel Collective Slack. En ese momento, me pidieron que lo convirtiera en una publicación de blog para que no se perdiera en el retroceso (DevRel Collective usa una cuenta gratuita de Slack, por lo que solo podemos retroceder 10,000 mensajes. Con más de 2000 miembros, eso sucede más rápido de lo que piensas. Pero estoy divagando.)

Por qué estas herramientas

Hagomucho desarrollo en Go. Realmente nunca pensé que lo haría, pero si has leído [esto] (/ publicaciones / categoría / programación / aventuras-en-golang) o [esto] (/ publicaciones / categoría / programación / ok-so-test-drive- desarrollo-es-una-cosa) o esto verás que me han chupado (Otra breve digresión. Sí, he oído hablar de Rust. Es el siguiente idioma de mi lista. Dame un minuto).

Go se ha convertido en mi idioma preferido porque ahora sé cómo hacer cosas rápidamente. También es fácil de ejecutar multiplataforma, por lo que puedo desarrollar en mi Mac e implementar en Linux y Just Works ™.

En la mayor parte de mi trabajo con Go, trato con datos en json y archivos de configuración en yaml o toml la mayor parte del tiempo, por lo que siempre es bienvenido encontrar herramientas que faciliten el manejo de esos datos.

Hay una herramienta de bonificación al final, si lees todo.

JSON primero

Al tratar con JSON, en Go, hay 3 cosas comunes que tengo que hacer todo el tiempo:

  • Validar el JSON
  • Desagrupar un objeto JSON en una estructura Go
  • Acceder a un campo en particular en un objeto JSON

Validar JSON

No siempre es necesario validar el JSON, pero suele ser una buena idea. No hay nada más frustrante que tratar de lidiar con algún objeto JSON solo para descubrir que lo que estás tratando de lidiar no es en realidad un JSON adecuado.

Entonces, primero valido el JSON usando jasonlint. Pegue su objeto json e inmediatamente le dirá si lo que tiene en sus manos es, de hecho, un objeto JSON válido.

Desagrupar un objeto JSON

Go tiene algunas formas prácticas de rally para lidiar con objetos JSON que lo convierten en mi opción para JSON Data. Si recibe un objeto JSON, por ejemplo, al consultar un sitio web para obtener datos a través de una API, puede usar


  err := json.Unmarshal(data, &myStruct)

Y los datos se descompondrán en su estructura de datos. Pero, y es un gran pero, debe tener una estructura de datos que coincida exactamente con sus datos entrantes.

Y ahí es donde entra json-to-go. Tome su objeto JSON validado, páselo en el lado izquierdo y fuera del derecho lado viene una estructura de datos Go válida para mantenerlo.


  {
    "name": "David",
    "ParentEmailAddress": "spam@davidgs.com",
    "letter": "I want a pony",
    "address": {
      "home": "Not your business where I live",
      "work": "Also probably none of your business, but Camunda"
    }
  }

Lo he ejecutado a través del linter JSON y es válido. Estupendo. Ahora, una estructura Go para sostenerlo:


  type Autogenerated struct {
    Name               string `json:"name"`
    Parentemailaddress string `json:"ParentEmailAddress"`
    Letter             string `json:"letter"`
    Address            struct {
      Home string `json:"home"`
      Work string `json:"work"`
    } `json:"address"`
  }

Y ahora puedo escribir un código de Go:


  import (
    "encoding/json"
    "log"
  )
  myStruct = Autogenerated{}
  data := getJsonData()
  err := json.Unmarshal(data, &myStruct)
  if err != nil {
    log.Fatal(err)
  }

Y myStruct será una estructura de datos debidamente completada con los datos de mi objeto JSON entrante.

Lo que me lleva a la tercera cosa que hago: acceder a campos específicos de un objeto JSON.

Accede a un campo particular de un objeto JSON

Con nuestra estructura JSON simple anterior, esto no sería un problema difícil en su mayor parte. ¿Quieres la dirección del trabajo?


  work_address = jsonObject.address.work

y estás ahí. Pero, ¿qué pasa si su objeto JSON es enormemente complicado?


  {
    "status": "OK",
    "results": [
      {
        "geometry": {
          "location": {
            "lat": 37.4229181,
            "lng": -122.0854212
          },
          "viewport": {
            "northeast": {
              "lat": 37.4242670803,
              "lng": -122.08407222
            },
            "southwest": {
              "lat": 37.4215691197,
              "lng": -122.08677018
            }
          },
          "location_type": "ROOFTOP"
        },
        "address_components": [
          {
            "long_name": "1600",
            "short_name": "1600",
            "types": [
              "street_number"
            ]
          },
          {
            "long_name": "Amphitheatre Pkwy",
            "short_name": "Amphitheatre Pkwy",
            "types": [
              "route"
            ]
          },
          {
            "long_name": "Mountain View",
            "short_name": "Mountain View",
            "types": [
              "locality",
              "political"
            ]
          },
          {
            "long_name": "Santa Clara",
            "short_name": "Santa Clara",
            "types": [
              "administrative_area_level_2",
              "political"
            ]
          },
          {
            "long_name": "California",
            "short_name": "CA",
            "types": [
              "administrative_area_level_1",
              "political"
            ]
          },
          {
            "long_name": "United States",
            "short_name": "US",
            "types": [
              "country",
              "political"
            ]
          },
          {
            "long_name": "94043",
            "short_name": "94043",
            "types": [
              "postal_code"
            ]
          }
        ],
        "formatted_address": "1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA",
        "types": [
          "street_address"
        ]
      }
    ]
}

Y eso ni siquiera es complejo en comparación con algunos con los que trato con regularidad, pero por el bien de la discusión, digamos que lo es. Y digamos que desea obtener el segundo type del address_component que, en el ejemplo anterior, es la ciudad, “Mountain View”. Bueno, los “resultados” son en realidad una matriz, aunque en el ejemplo anterior solo tiene un elemento. Si la matriz tiene muchos elementos y desea obtener el resultado del tercer elemento de la matriz, se vuelve un poco más difícil.

Aquí es donde entra json selector. Pegue su objeto JSON (que ya sabemos que es válido, porque lo ha validado, * ¿verdad *?) Y luego haga clic en lo que quieres.


['results'][0]['address_components'][2]['types'][1]

¡Oh cielos! ¡Hay, hay! Es el primer elemento en la matriz results, es el campo address_components, y el tercer elemento enesa matriz, ¡y es el segundo elemento de la matriz types!

Recuerde, la numeración de matrices comienza en cero.

Un conjunto de herramientas completo

Y ese es un conjunto de herramientas completo para tratar con objetos JSON. Pero espera, mencioné “yaml” ¡y aún no he hablado de eso!

¡Correcto! ¡Y gracias por prestar atención!

YAML

YAML: otro lenguaje de marcado. Porque lo que el mundo necesita son más lenguajes de marcado. Pero yaml es muy útil para los archivos de configuración. Utilizo archivos de configuración basados en yaml para todo tipo de cosas. El archivo de configuración para construir todo este sitio web es un archivo yaml.

Por lo tanto, es importante poder analizar y tratar correctamente un archivo yaml.

Nuevamente, con Go, es relativamente sencillo. Mi primera herramienta es yaml2go. Al igual que json-to-go, tomará su estructura de archivo yaml y la convertirá en estructuras de datos Go.


  # Paste your yaml here...
  kind: test
  metadata:
    name: cluster
    namespace: test-ns

And get yourself some spiffy Go code:

// MyYaml
type MyYaml struct {
  Kind     string   `yaml:"kind"`
  Metadata Metadata `yaml:"metadata"`
}

// Metadata
type Metadata struct {
  Name      string `yaml:"name"`
  Namespace string `yaml:"namespace"`
}

Y luego puede deshacer su archivo de configuración:


  import (
    "io/ioutil"
    "log"
    "gopkg.in/yaml.v2"
  )
  var config MyYaml
  // InitConfig reads the config file and sets up the config struct
  func InitConfig(){
    dat, err := ioutil.ReadFile("./config.yaml")
    if err != nil {
      log.Fatal("No startup file: ", err)
    }
    err = yaml.Unmarshal(dat, &config)
    if err != nil {
      log.Fatal(err)
    }
  }

Y maneje sus variables de configuración desde allí. Cuando haya terminado, si desea escribir su archivo config.yaml (ya sabe, si su programa en ejecución cambió algún parámetro de inicio), puede escribirlo todo después de ordenarlo:


  import (
    "io/ioutil"
    "log"
    "gopkg.in/yaml.v2"
  )
  // WriteDictators outputs the entire config file
  func WriteConfig() {
    newConf, err := yaml.Marshal(config)
    if err != nil {
      log.Fatal(err)
    }
    err = ioutil.WriteFile("./config.yaml", newConf, 0644)
    if err != nil {
      panic(err)
    }
  }

Ahora ha sobrescrito su antiguo archivo de configuración con su nuevo archivo de configuración. Es esencialmente lo mismo que JSON, solo para yaml.

¡Tiempo extra de herramientas!

Este me ha salvado el tocino en más de una ocasión. Porque realmente, ** realmente * apesta escribir expresiones regulares (RegEx). Como realmente. Soy terrible en eso. Tan terrible en eso que hasta que encontrara esta herramienta, escribiría 30 líneas de código Go en una sola llamada a una expresión regular.

No. Alguna. ¡Más!

Ingrese RegEx101. Ni siquiera puedo empezar a decir lo brillante que es esta herramienta. Dejame explicar.

Puede (o no) notar que este sitio web está traducido a varios idiomas. No hablo de ninguno de ellos. No escribo ninguno de ellos. Pero escribo Go. Entonces, usando Go y las API de traducción de Google, puedo traducir automáticamente cualquier publicación aquí a los idiomas que desee.

Pero la API de traducción hace algunos errores graves con partes de un archivo Markdown. Entonces tuve que compensar. Las URL son un gran ejemplo. Si mi archivo blog-post.md tiene un enlace[mi enlace](https://my.link), entonces Google Translate traducirá felizmente[mi enlace], lo cual quiero, luego agregar un espacio entre ] (, que no quiero, y luego (a veces) traducir los componentes de la ruta del enlace, que definitivamente no quiero.

Entonces tuve que compensar, como dije. Al principio, hice esto manualmente y fue un verdadero desastre. Era propenso a errores y difícil de mantener. Pero luego encontré RexEx101 y todo cambió.

Yo podría hacer esto: Una expresión regular que encuentra la URL en el párrafo anterior

¡Y luego incluso explica lo que hacen las partes varoius de la expresión regular!

explicación de qué partes de la expresión regular coinciden

Entonces puedo hacer que mi programa Go busque todas las URL en un fragmento de texto dado, y luego, una vez traducido el texto, reemplace todos los ](original/url) con ] (original / url)


  // fix URLs because google translate changes [link](http://your.link) to
  // [link] (http://your.link) and it *also* will translate any path
  // components, thus breaking your URLs.
  reg := regexp.MustCompile(`]\([-a-zA-Z0-9@:%._\+~#=\/]{1,256}\)`)
  // get all the URLs with a single RegEx, keep them for later.
  var foundUrls [][]byte = reg.FindAll([]byte(xlate), -1)
  ...
  // translate the bit
  // Now it's time to go back and replace all the fucked up urls ...
  reg = regexp.MustCompile(`] \([-a-zA-Z0-9@:%._\+~#=\/ ]{1,256}\)`)
  for x := 0; x < len(foundUrls); x++ {
    tmp := reg.FindIndex([]byte(translated))
    if tmp == nil {
      break
    }
    t := []byte(translated)
    translated = fmt.Sprintf("%s(%s%s", string(t[0:tmp[0]+1]), string(foundUrls[x][2:]), (string(t[tmp[1]:])))
  }

También podría usarlo para encontrar el otro extremo: regex para encontrar las URL desordenadas

Y siempre explica de manera útil lo que está haciendo:

explicación de cómo la expresión regular coincide con varias partes del texto de entrada

¡Oh mi! También podría arreglar las otras cosas que Google Trnaslate estropea, como negrita texto ** negrita ** se convierte en ** negrita ** que ya no es negrita. Pero con esto:


// a bunch of regexs to fix other broken stuff
	reg = regexp.MustCompile(` (\*\*) ([A-za-z0-9]+) (\*\*)`) // fix bolds (**foo**)
	translated = string(reg.ReplaceAll([]byte(translated), []byte(" $1$2$3")))
	reg = regexp.MustCompile(`&quot;`) // fix escaped quotes
	translated = string(reg.ReplaceAll([]byte(translated), []byte("\"")))
	reg = regexp.MustCompile(`&gt;`) //fix >
	translated = string(reg.ReplaceAll([]byte(translated), []byte(">")))
	reg = regexp.MustCompile(`&lt;`) // fix <
	translated = string(reg.ReplaceAll([]byte(translated), []byte("<")))
	reg = regexp.MustCompile(`&#39;`) // fix '
	translated = string(reg.ReplaceAll([]byte(translated), []byte("'")))
	reg = regexp.MustCompile(` (\*) ([A-za-z0-9]+) (\*)`) // fix underline (*foo*)
	translated = string(reg.ReplaceAll([]byte(translated), []byte("$1$2$3")))

Brilliant. Becaues I never would have been able to write those Regular Expressions without serious help!

Conclusion

I hope you found these tools as helpful as I do. I really do use them on a near-daily basis for the work I do. I have them pinned as tabs in my browser so they are always open and easy to find.

If you know of other tools, please do let me know! And don’t forget to follow me on Twitter!