Dit begon als een eenvoudig bericht naar een interne Slack-groep. Toen werd ik gevraagd om het op een ander kanaal in die Slack te plaatsen. Toen besloot ik het op de DevRel Collective Slack te plaatsen. Op dat moment werd mij gevraagd om er een blogpost van te maken, zodat het niet verloren ging in het terugscrollen (DevRel Collective gebruikt een gratis Slack-account, dus we kunnen maar 10.000 berichten terugscrollen. Met meer dan 2.000 leden gaat dat sneller dan je denkt. Maar ik dwaal af.)

Waarom deze tools

Ik doeveel aan ontwikkeling in Go. Ik had echt nooit gedacht dat ik het zou doen, maar als je this of this development-is-a-thing) of this je zult zien dat ik ben stomdronken in. (Nog een korte uitweiding. Ja, ik heb van Rust gehoord. Het is de volgende taal op mijn lijst. Geef me een minuutje.)

Go is mijn go-to-taal geworden omdat ik nu weet hoe ik er snel dingen in moet doen. Het is ook gemakkelijk om platformonafhankelijk uit te voeren, zodat ik kan ontwikkelen op mijn Mac en kan worden geïmplementeerd op Linux en it Just Works ™.

In het grootste deel van mijn werk met Go, heb ik te maken met gegevens in json en configuratiebestanden in yaml of toml meestal, dus het vinden van tools om het omgaan met die gegevens gemakkelijker te maken, is altijd welkom!

Aan het einde is er een bonus-tool, als je alles leest.

JSON eerst

Bij het omgaan met JSON, in Go, zijn er 3 veelvoorkomende dingen die ik altijd moet doen:

  • Valideer de JSON
  • Unmarshall een JSON-object in een Go-structuur
  • Toegang tot een bepaald veld in een JSON-object

Valideer JSON

Het valideren van de JSON is niet altijd vereist, maar het is meestal een goed idee. Niets zo frustrerend als het proberen om met een JSON-object om te gaan, alleen om te ontdekken dat datgene waarmee je ** probeert om te gaan, niet echt de juiste JSON is.

Dus ik valideer de JSON eerst met jasonlint. Plak je json-object en het zal je onmiddellijk vertellen of wat je in handen hebt, in feite een geldig JSON-object is.

Marshall een JSON-object

Go heeft een aantal handige manieren om met JSON-objecten om te gaan, waardoor het mijn go-to is voor JSON-gegevens. Als u een JSON-object krijgt, bijvoorbeeld door via een API op een website naar gegevens te zoeken, kunt u


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

En de gegevens blijven ongehinderd in uw datastructuur. Maar, en het is een grote maar, je moet een datastructuur hebben die exact overeenkomt met je inkomende data.

En dat is waar json-to-go binnenkomt. Neem je gevalideerde JSON-object, voorbij het aan de linkerkant en aan de rechterkant kant komt een geldige Go-datastructuur om het vast te houden.


  {
    "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"
    }
  }

Ik heb dat door de JSON-linter gehaald en het is geldig. Super goed. Nu, een Go-structuur om het vast te houden:


  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"`
  }

En nu kan ik wat Go-code schrijven:


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

En myStruct zal een correct ingevulde gegevensstructuur zijn met de gegevens van mijn inkomende JSON-object.

Dat brengt me bij het derde wat ik doe: toegang krijgen tot specifieke velden van een JSON-object.

Toegang tot een bepaald veld van een JSON-object

Met onze eenvoudige JSON-structuur hierboven zou dit voor het grootste deel geen moeilijk probleem zijn. Wil je het werkadres?


  work_address = jsonObject.address.work

en je bent er. Maar wat als uw JSON-object enorm gecompliceerd is?


  {
    "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"
        ]
      }
    ]
}

En dat is niet eens ingewikkeld in vergelijking met sommige waar ik regelmatig mee te maken heb, maar laten we zeggen dat het dat wel is. En laten we zeggen dat u het tweede type uit het address_component wilt halen dat, in het bovenstaande voorbeeld, de stad “Mountain View” is. Welnu, de results is eigenlijk een array, hoewel het in het bovenstaande voorbeeld maar één element heeft. Als de array veel elementen bevat en u het resultaat van het derde item in de array wilt, wordt het een beetje moeilijker.

Dit is waar json selector binnenkomt. Plak je JSON-object in (waarvan we al weten dat het geldig is, omdat je het hebt gevalideerd,juist?) En klik vervolgens op wat jij wil.


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

Oh hemel! Er is! Het is het eerste element in de results array, het is het address_components veld, en het derde element inthat array, en het is het tweede element van de types array!

Onthoud dat arraynummering begint bij nul!

Een complete toolset

En dat is een complete toolset voor het omgaan met JSON-objecten. Maar wacht, ik noemde yaml en ik heb er nog niet over gesproken!

Correct! En bedankt voor het opletten!

YAML

YAML: Another Markup Language. Omdat de wereld meer opmaaktalen nodig heeft. Maar yaml is super handig voor configuratiebestanden. Ik gebruik op yaml gebaseerde configuratiebestanden voor allerlei dingen. Het configuratiebestand voor het bouwen van deze hele website is een yaml-bestand.

Het is dus belangrijk om in staat te zijn om een yaml-bestand correct te ontleden en ermee om te gaan.

Nogmaals, met Go is het relatief eenvoudig. Mijn eerste tool is yaml2go. Net als json-to-go, zal het uw yaml bestandsstructuur veranderen in Go-datastructuren.


  # 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"`
}

En dan kun je je configuratiebestand ongedaan maken:


  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)
    }
  }

En behandel uw configuratievariabelen van daaruit. Als je klaar bent en je je config.yaml -bestand wilt wegschrijven (je weet wel, als je actieve programma opstartparameters heeft gewijzigd), kun je het allemaal wegschrijven nadat je het hebt gerangschikt:


  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)
    }
  }

Je hebt nu je oude configuratiebestand overschreven met je nieuwe configuratiebestand. Het is in wezen hetzelfde als JSON, alleen voor yaml.

Bonus tool tijd!

Deze heeft mijn spek meer dan eens gered. Omdat ik echt ** echt * stom ben in het schrijven van reguliere expressies (RegEx). Ik bedoel, serieus. Ik ben er vreselijk in. Zo vreselijk dat ik, totdat ik dit hulpmiddel vond, 30 regels Go-code zou schrijven naar ongeveer een enkele aanroep naar een regex.

Niet. Ieder. Meer!

Voer RegEx101 in. Ik kan niet eens beginnen te zeggen hoe briljant deze tool is. Laat het me uitleggen.

U zult (of misschien niet) opmerken dat deze website in een heleboel talen is vertaald. Ik spreek er geen van. Ik schrijf er geen van. Maar ik schrijf wel Go. Dus, met behulp van de vertaal-API’s van Go en Google, kan ik elk bericht hier automatisch vertalen in de taal / talen die ik wil.

Maar de vertaal-API doet een aantal ernstige dingen in de war met delen van een Markdown-bestand. Dus ik moest compenseren. URL’s zijn een goed voorbeeld. Als mijn blog-post.md-bestand een link bevat ‘mijn link’, dan zal Google Translate graag ‘[mijn link]’ vertalen, wat ik wil, en dan een spatie tussen ] (, wat ik niet wil, en vertaal dan (soms) de padcomponenten van de link, die ik zeker niet wil.

Dus ik moest compenseren, zoals ik al zei. In eerste instantie deed ik dit handmatig, en het was een echte puinhoop. Het was foutgevoelig en moeilijk te onderhouden. Maar toen vond ik RexEx101 en alles veranderde.

Ik zou dit kunnen doen: Een regex die de url in de bovenstaande alinea vindt

En dan legt het zelfs uit wat de varoius-delen van de regex doen!

uitleg van welke delen van de regex overeenkomen

Dus dan kan ik mijn Go-programma alle URL’s in een bepaald stuk tekst laten zoeken, en dan, nadat de tekst is vertaald, alle ](original/url) vervangen door ] (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]:])))
  }

Ik zou het ook kunnen gebruiken om het andere uiteinde te vinden: regex om de verknoeide URL’s te vinden

En het legt altijd nuttig uit wat het doet:

uitleg over hoe de regex overeenkwam met verschillende delen van de invoertekst

Oh mijn! Ik zou ook de andere dingen kunnen repareren die Google Trnaslate verknoeit, zoals vetgedrukte tekst `** vet ** ‘wordt’ vetgedrukt ‘wat niet meer vetgedrukt is. Maar hiermee:


// 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!