Obsah
1. Serializace a deserializace datových struktur v programovacím jazyce Go
2. Rozhraní s předpisem metod pro serializaci a deserializaci dat
3. Krátké zopakování – práce s formátem JSON
4. Některá úskalí převodu hodnot do formátu JSON
5. Serializace dat do formátu XML
6. Specifikace jména kořenového uzlu, konfigurace odsazení při formátování XML
7. Struktura XML odlišná od struktury původních serializovaných dat
8. Serializace polí, speciální hodnoty, ukazatele apod.
9. Serializace sekvence struktur
10. Obalení sekvence struktur dalším datovým typem
11. Vylepšení předchozích příkladů
12. Serializace jedné struktury jak do JSONu, tak i do XML – problematika anotačních řetězců
13. Binární formáty a programovací jazyk Go
15. Serializace datové struktury do formátu gob
16. Formát CBOR (Concise Binary Object Representation)
17. Serializace dat do formátu BSON
18. Deserializace dat z formátu BSON
19. Repositář s demonstračními příklady
1. Serializace a deserializace datových struktur v programovacím jazyce Go
V dnešní části seriálu o programovacím jazyku Go se zaměříme na popis způsobů použití různých formátů určených pro serializaci a deserializaci dat s jejich případným přenosem do jiné aplikace či služby (přenosem se myslí jak lokální komunikace, tak i přenos do služby běžící na jiném počítači). Již dříve jsme se ve stručnosti seznámili s využitím formátu JSON a nepřímo taktéž s formátem TOML používaným typicky pro konfigurační soubory (a mnohem méně často pro rozsáhlejší data). V případě JSONu se jedná o poměrně důležitý formát, protože JSON (a samozřejmě též XML) se v současnosti používá v mnoha webových službách a i když stále vznikají a jsou postupně adaptovány další formáty, ať již textové (YAML, edn) či binární (BSON, B-JSON, Smile, Protocol-Buffers), CBOR atd., je velmi pravděpodobné, že se JSON bude i nadále poměrně masivně využívat. Nicméně pochopitelně existují situace, v nichž je vhodné textový a relativně neúsporný JSON nahradit právě nějakým binárním formátem.
Některé metody serializace a deserializace datových struktur jsou implementovány přímo ve standardních knihovnách programovacího jazyka Go; další metody (resp. přesněji řečeno formáty) však již vyžadují instalaci zvláštní knihovny, popř. vlastní implementaci daného formátu. Nejdříve se zaměříme na ty formáty, které jsou podporovány bez nutnosti instalace dalších balíčků a posléze si ukážeme i některé přídavné balíčky pro ty nejzajímavější popř. nejpoužívanější formáty.
2. Rozhraní s předpisem metod pro serializaci a deserializaci dat
Základní rozhraní pro serializaci a deserializaci datových struktur jsou definována ve standardním balíčku encoding, jehož dokumentaci je možné nalézt na adrese https://golang.org/pkg/encoding/. Každé z rozhraní předepisuje – jak je ostatně v jazyce Go zvykem – pouze jednu metodu. Jakýkoli objekt, který je schopen své serializace či deserializace může tyto metody obsahovat:
# | Rozhraní | Signatura metody |
---|---|---|
1 | TextMarshaler | MarshalText() (text []byte, err error) |
2 | TextUnmarshaler | UnmarshalText(text []byte) error |
3 | BinaryMarshaler | MarshalBinary() (data []byte, err error) |
4 | BinaryUnmarshaler | UnmarshalBinary(data []byte) error |
Typické je, že při serializaci (marshalingu) je výsledkem řez bajtů a nikoli řetězec (ve smyslu jazyka Go). Podobně při deserialiaci (umarshalingu) je zdrojem dat parametr typu řez bajtů. Díky tomu jsme odstíněni od problematiky kódování znaků ve „skutečných“ textových řetězcích.
3. Krátké zopakování – práce s formátem JSON
Před popisem dalších metod serializace a deserializace datových struktur si krátce zopakujme, jakým způsobem je ve standardní knihovně programovacího jazyka Go podporován formát JSON. Tento formát je dnes široce rozšířen a používá se jak pro ukládání konfigurací, specifikaci schématu v OpenAPI (vedle YAMLu), uložení konfigurace dashboardu v Grafaně atd., tak – a to možná především – ve webových službách a aplikacích pro přenos strukturovaných dat. Jedná se o formát, jehož syntaxe a sémantika je odvozená od JavaScriptu, s čímž je nutné počítat, protože ne všechny knihovny například umožňují, aby se v klíčích objevovaly pomlčky či jiné „podivné“ znaky, i když to teoreticky formát JSON umožňuje.
Podívejme se na příklad služby vracející dokument reprezentovaný ve formátu JSON (tuto službu můžete použít pro testování apod.):
$ curl http://httpbin.org/json { "slideshow": { "author": "Yours Truly", "date": "date of publication", "slides": [ { "title": "Wake up to WonderWidgets!", "type": "all" }, { "items": [ "Why <em>WonderWidgets</em> are great", "Who <em>buys</em> WonderWidgets" ], "title": "Overview", "type": "all" } ], "title": "Sample Slide Show" } }
Formát JSON umožňuje uložení a tím pádem i přenos jediné (nijak nepojmenované) hodnoty. Podporovány jsou přitom hodnoty, které můžeme zařadit do šesti kategorií (viz též příslušná část graficky vyjádřené syntaxe formátu JSON):
# | Hodnota | Stručný popis |
---|---|---|
1 | string | řetězec (s plnou podporou Unicode) |
2 | number | celé číslo, popř. hodnota typu double |
3 | object | ve skutečnosti se jedná o asociativní pole (mapu), viz poznámka v úvodní kapitole |
4 | array | pole, ovšem v JSONu nemusí mít všechny prvky pole stejný typ |
5 | true, false | pravdivostní hodnota |
6 | null | prázdná hodnota |
Pro převod libovolného typu (přesněji řečeno hodnoty libovolného typu) do JSONu se používá funkce nazvaná Marshal, kterou nalezneme v balíčku encoding/json:
func Marshal(v interface{}) ([]byte, error)
Povšimněte si, že tato funkce skutečně akceptuje hodnotu libovolného typu, protože prázdné rozhraní implementuje (zcela automaticky!) každý datový typ (s tímto zajímavým konceptem „univerzálního datového typu“ se ještě několikrát setkáme, zejména v rozhraních mezi Go a dalšími systémy). Návratovou hodnotou je sekvence bajtů (nikoli řetězec!) a popř. i struktura reprezentující chybový stav, pokud k chybě skutečně došlo. V opačném případě se ve druhé návratové hodnotě funkce Marshal vrací nil, jak jsme ostatně zvyklí ze všech podobně koncipovaných funkcí.
V typických zdrojových kódech se tedy setkáme s tímto idiomatickým zápisem:
json_bytes, err := json.Marshal(a) if err != nil { log.Fatal(err) } ... ... ...
4. Některá úskalí převodu hodnot do formátu JSON
V některých případech, například při přenosu výsledků z různých simulací, měření, výpočtů apod. je nutné pracovat s celočíselnými hodnotami, popř. s hodnotami s plovoucí řádovou čárkou. A právě zde můžeme narazit na různá úskalí, které se týkají speciálních hodnot typu +Inf, -Inf (kladné a záporné nekonečno) a pochopitelně taktéž NaN (výsledkem nějaké operace není skutečné číslo, výsledek nelze vyjádřit atd.). Nejprve se podívejme na serializaci běžných hodnot s plovoucí řádovou čárkou. Podporován by měl být rozsah i přesnost odpovídající typu double, resp. v programovacím jazyku Go typu float64:
package main import ( "encoding/json" "fmt" ) func main() { var a float64 = 0.0 var b float64 = 1e10 var c float64 = 1e100 var d float64 = 2.3e-50 var jsonOutput []byte jsonOutput, _ = json.Marshal(a) fmt.Println(string(jsonOutput)) jsonOutput, _ = json.Marshal(b) fmt.Println(string(jsonOutput)) jsonOutput, _ = json.Marshal(c) fmt.Println(string(jsonOutput)) jsonOutput, _ = json.Marshal(d) fmt.Println(string(jsonOutput)) }
V tomto případě nedochází k chybám (ostatně chybové hodnoty zcela ignorujeme) a serializace do JSONu vypadá následovně:
0 10000000000 1e+100 2.3e-50
Ovšem u výše uvedených speciálních hodnot dochází k problémům, což je zmíněno například na Stack Overflow. Můžeme se o tom přesvědčit nepatrnou úpravou předchozího příkladu:
package main import ( "encoding/json" "fmt" "math" ) func main() { var a float64 = -0.0 var b float64 = math.NaN() var c float64 = -math.NaN() var d float64 = math.Inf(1) var e float64 = math.Inf(-1) var jsonOutput []byte jsonOutput, _ = json.Marshal(a) fmt.Println(string(jsonOutput)) jsonOutput, _ = json.Marshal(b) fmt.Println(string(jsonOutput)) jsonOutput, _ = json.Marshal(c) fmt.Println(string(jsonOutput)) jsonOutput, _ = json.Marshal(d) fmt.Println(string(jsonOutput)) jsonOutput, _ = json.Marshal(e) fmt.Println(string(jsonOutput)) }
Tentokrát se zdá, že serializace do formátu JSON vrátila pouze prázdné řetězce:
0
Ovšem skutečnost je jiná, protože balíček encoding/json hlídá, které hodnoty lze převést a které nikoli. Musíme ovšem naši aplikaci naprogramovat korektně, tj. v tomto konkrétním případě reagovat na všechny chybové stavy:
package main import ( "encoding/json" "fmt" "math" ) func main() { var a float64 = -0.0 var b float64 = math.NaN() var c float64 = -math.NaN() var d float64 = math.Inf(1) var e float64 = math.Inf(-1) var jsonOutput []byte var err error jsonOutput, err = json.Marshal(a) fmt.Println(err, string(jsonOutput)) jsonOutput, err = json.Marshal(b) fmt.Println(err, string(jsonOutput)) jsonOutput, err = json.Marshal(c) fmt.Println(err, string(jsonOutput)) jsonOutput, err = json.Marshal(d) fmt.Println(err, string(jsonOutput)) jsonOutput, err = json.Marshal(e) fmt.Println(err, string(jsonOutput)) }
Nyní již bude po spuštění příkladu patrné, že došlo k chybám při serializaci speciálních hodnot:
0 json: unsupported value: NaN json: unsupported value: NaN json: unsupported value: +Inf json: unsupported value: -Inf
Ještě si ukažme, jakým způsobem je možné do JSONu uložit pole s prvky různých typů, včetně dvojrozměrných polí (což v JSONu není nic jiného, než pole polí):
package main import ( "encoding/json" "fmt" ) func main() { var a1 [10]byte var a2 [10]int32 a3 := [10]int32{1, 10, 2, 9, 3, 8, 4, 7, 5, 6} a4 := []string{"www", "root", "cz"} a5 := []interface{}{1, "root", 3.1415, true, []int{1, 2, 3, 4}} matice := [4][3]float32{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {0, -1, 0}, } a1_json, _ := json.Marshal(a1) fmt.Println(string(a1_json)) a2_json, _ := json.Marshal(a2) fmt.Println(string(a2_json)) a3_json, _ := json.Marshal(a3) fmt.Println(string(a3_json)) a4_json, _ := json.Marshal(a4) fmt.Println(string(a4_json)) a5_json, _ := json.Marshal(a5) fmt.Println(string(a5_json)) matice_json, _ := json.Marshal(matice) fmt.Println(string(matice_json)) }
Výsledky budou vypadat následovně:
[0,0,0,0,0,0,0,0,0,0] [0,0,0,0,0,0,0,0,0,0] [1,10,2,9,3,8,4,7,5,6] ["www","root","cz"] [1,"root",3.1415,true,[1,2,3,4]] [[1,2,3],[4,5,6],[7,8,9],[0,-1,0]]
5. Serializace dat do formátu XML
Ve druhé části článku si ukážeme způsoby serializace dat (tedy prakticky libovolné datové struktury) do formátu XML. Zcela nejjednodušší demonstrační příklad, v němž se pokusíme serializovat hodnoty typu User1 a User2, bude vypadat následovně:
package main import ( "encoding/xml" "fmt" ) type User1 struct { id uint32 name string surname string } type User2 struct { Id uint32 Name string Surname string } func main() { user1 := User1{ 1, "Pepek", "Vyskoč"} user2 := User2{ 1, "Pepek", "Vyskoč"} user1asXML, _ := xml.Marshal(user1) fmt.Println(string(user1asXML)) fmt.Println() user2asXML, _ := xml.Marshal(user2) fmt.Println(string(user2asXML)) }
Podívejme se nyní na výsledek běhu tohoto příkladu:
<User1></User1> <User2><Id>1</Id><Name>Pepek</Name><Surname>Vyskoč</Surname></User2>
Podobně jako při exportu do JSONu lze i při exportu do formátu XML specifikovat jména uzlů (v JSONu jména atributů, tedy klíče). Slouží k tomu speciálně naformátované řetězce přidané k jednotlivým prvkům a začínající prefixem „xml:“. Celý příklad si tedy nepatrně upravíme následujícím způsobem:
package main import ( "encoding/xml" "fmt" ) type User1 struct { id uint32 `xml:"id"` name string `xml:"user_name"` surname string `xml:"surname"` } type User2 struct { Id uint32 `xml:"id"` Name string `xml:"user_name"` Surname string `xml:"surname"` } func main() { user1 := User1{ 1, "Pepek", "Vyskoč"} user2 := User2{ 1, "Pepek", "Vyskoč"} user1asXML, _ := xml.Marshal(user1) fmt.Println(string(user1asXML)) fmt.Println() user2asXML, _ := xml.Marshal(user2) fmt.Println(string(user2asXML)) }
Výsledek běhu takto upraveného příkladu:
<User1></User1> <User2><id>1</id><user_name>Pepek</user_name><surname>Vyskoč</surname></User2>
6. Specifikace jména kořenového uzlu, konfigurace odsazení při formátování XML
Na rozdíl od formátu JSON, v němž neexistuje koncept kořenového uzlu (přenáší se jen hodnota jediného objektu, nikoli i jeho název), je v XML použit právě kořenový uzel. V předchozích příkladech bylo jméno tohoto uzlu odvozeno od jména serializované datové struktury (User1 nebo User2), což ovšem nemusí být ve všech případech vyhovující. Ovšem relativně snadno je možné tento nedostatek napravit, a to následujícím způsobem – použitím nového prvku typu xml.Name se specifikovaným jménem. Upravený a vylepšený demonstrační příklad vypadá následovně:
package main import ( "encoding/xml" "fmt" ) type User1 struct { XMLName xml.Name `xml:"user"` id uint32 `xml:"id"` name string `xml:"user_name"` surname string `xml:"surname"` } type User2 struct { XMLName xml.Name `xml:"user"` Id uint32 `xml:"id"` Name string `xml:"user_name"` Surname string `xml:"surname"` } func main() { user1 := User1{ id: 1, name: "Pepek", surname: "Vyskoč"} user2 := User2{ Id: 1, Name: "Pepek", Surname: "Vyskoč"} user1asXML, _ := xml.Marshal(user1) fmt.Println(string(user1asXML)) fmt.Println() user2asXML, _ := xml.Marshal(user2) fmt.Println(string(user2asXML)) }
Tento příklad si samozřejmě spustíme. Z výsledku je patrné, že se skutečně změnilo i jméno kořenového uzlu:
<user></user> <user><id>1</id><user_name>Pepek</user_name><surname>Vyskoč</surname></user>
V některých případech je požadováno, aby výsledné XML bylo korektně naformátováno, což se hodí zejména při práci s relativně krátkými konfiguračními soubory atd. Naformátování výsledného XML zajišťuje metoda nazvaná MarshalIndent, které se navíc předá prefix všech řádků (může se jednat o prázdný řetězec) a libovolná sekvence znaků použitá při odsazování vnořených uzlů. Zde můžeme použít například čtyři mezery, znak Tab atd.:
package main import ( "encoding/xml" "fmt" ) type User1 struct { XMLName xml.Name `xml:"user"` id uint32 `xml:"id"` name string `xml:"user_name"` surname string `xml:"surname"` } type User2 struct { XMLName xml.Name `xml:"user"` Id uint32 `xml:"id"` Name string `xml:"user_name"` Surname string `xml:"surname"` } func main() { user1 := User1{ id: 1, name: "Pepek", surname: "Vyskoč"} user2 := User2{ Id: 1, Name: "Pepek", Surname: "Vyskoč"} user1asXML, _ := xml.MarshalIndent(user1, "", " ") fmt.Println(string(user1asXML)) fmt.Println() user2asXML, _ := xml.MarshalIndent(user2, "", " ") fmt.Println(string(user2asXML)) fmt.Println() user2asXML, _ = xml.MarshalIndent(user2, "", "\t") fmt.Println(string(user2asXML)) fmt.Println() user2asXML, _ = xml.MarshalIndent(user2, "\t", "\t") fmt.Println(string(user2asXML)) }
Zde nás bude zajímat vygenerovaný výsledek, který má tvar:
<user></user> <user> <id>1</id> <user_name>Pepek</user_name> <surname>Vyskoč</surname> </user> <user> <id>1</id> <user_name>Pepek</user_name> <surname>Vyskoč</surname> </user> <user> <id>1</id> <user_name>Pepek</user_name> <surname>Vyskoč</surname> </user>
7. Struktura XML odlišná od struktury původních serializovaných dat
Při serializaci datových struktur do formátu XML je možné zvolit odlišnou strukturu výsledného souboru. Pokud například namísto následující struktury:
<user> <id>1</id> <user_name>Pepek</user_name> <surname>Vyskoč</surname> </user>
Budeme požadovat, aby výsledný soubor XML vypadal odlišně – jméno a příjmení má být ve zvláštním poduzlu:
<user> <id>1</id> <name> <first>Pepek</first> <last>Vyskoč</last> </name> </user>
Tohoto chování lze docílit odlišnou specifikací dekorátoru, který je zapsaný za každou datovou položkou, která má být serializována:
type User2 struct { XMLName xml.Name `xml:"user"` Id uint32 `xml:"id"` Name string `xml:"name>first"` Surname string `xml:"name>last"` }
Upravený zdrojový kód tohoto demonstračního příkladu vypadá takto:
package main import ( "encoding/xml" "fmt" ) type User1 struct { XMLName xml.Name `xml:"user"` id uint32 `xml:"id"` name string `xml:"name>first"` surname string `xml:"name>last"` } type User2 struct { XMLName xml.Name `xml:"user"` Id uint32 `xml:"id"` Name string `xml:"name>first"` Surname string `xml:"name>last"` } func main() { user1 := User1{ id: 1, name: "Pepek", surname: "Vyskoč"} user2 := User2{ Id: 1, Name: "Pepek", Surname: "Vyskoč"} user1asXML, _ := xml.MarshalIndent(user1, "", " ") fmt.Println(string(user1asXML)) fmt.Println() user2asXML, _ := xml.MarshalIndent(user2, "", " ") fmt.Println(string(user2asXML)) fmt.Println() user2asXML, _ = xml.MarshalIndent(user2, "", "\t") fmt.Println(string(user2asXML)) fmt.Println() user2asXML, _ = xml.MarshalIndent(user2, "\t", "\t") fmt.Println(string(user2asXML)) }
Výsledek běhu příkladu (všechny varianty formátování):
<user></user> <user> <id>1</id> <name> <first>Pepek</first> <last>Vyskoč</last> </name> </user> <user> <id>1</id> <name> <first>Pepek</first> <last>Vyskoč</last> </name> </user> <user> <id>1</id> <name> <first>Pepek</first> <last>Vyskoč</last> </name> </user>
Pochopitelně je možné určit, které hodnoty mají být uloženy ve formě pojmenovaných atributů. Podívejme se na následující demonstrační příklad:
package main import ( "encoding/xml" "fmt" ) type User1 struct { XMLName xml.Name `xml:"user"` id uint32 `xml:"id,attr"` name string `xml:"name&first,attr"` surname string `xml:"name&last,attr"` } type User2 struct { XMLName xml.Name `xml:"user"` Id uint32 `xml:"id,attr"` Name string `xml:"name&first"` Surname string `xml:"name&last"` } func main() { user1 := User1{ id: 1, name: "Pepek", surname: "Vyskoč"} user2 := User2{ Id: 1, Name: "Pepek", Surname: "Vyskoč"} user1asXML, _ := xml.MarshalIndent(user1, "", " ") fmt.Println(string(user1asXML)) fmt.Println() user2asXML, _ := xml.MarshalIndent(user2, "", " ") fmt.Println(string(user2asXML)) fmt.Println() user2asXML, _ = xml.MarshalIndent(user2, "", "\t") fmt.Println(string(user2asXML)) fmt.Println() user2asXML, _ = xml.MarshalIndent(user2, "\t", "\t") fmt.Println(string(user2asXML)) }
Výsledek:
<user></user> <user id="1"> <name> <first>Pepek</first> <last>Vyskoč</last> </name> </user> <user id="1"> <name> <first>Pepek</first> <last>Vyskoč</last> </name> </user> <user id="1"> <name> <first>Pepek</first> <last>Vyskoč</last> </name> </user>
8. Serializace polí, speciální hodnoty, ukazatele apod.
Pro úplnost se ještě podívejme na způsob serializace polí. Následující demonstrační příklad vypadá podobně, jako již výše uvedený příklad na serializaci do formátu JSON, pouze se provede uložení do souboru ve formátu XML:
package main import ( "encoding/xml" "fmt" ) func main() { var a1 [10]byte var a2 [10]int32 a3 := [10]int32{1, 10, 2, 9, 3, 8, 4, 7, 5, 6} a4 := []string{"www", "root", "cz"} a5 := []interface{}{1, "root", 3.1415, true, []int{1, 2, 3, 4}} matice := [4][3]float32{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {0, -1, 0}, } a1asXML, _ := xml.Marshal(a1) fmt.Println(string(a1asXML)) a2asXML, _ := xml.Marshal(a2) fmt.Println(string(a2asXML)) a3asXML, _ := xml.Marshal(a3) fmt.Println(string(a3asXML)) a4asXML, _ := xml.Marshal(a4) fmt.Println(string(a4asXML)) a5asXML, _ := xml.Marshal(a5) fmt.Println(string(a5asXML)) maticeasXML, _ := xml.Marshal(matice) fmt.Println(string(maticeasXML)) }
U polí jsme nijak nespecifikovali jména prvků, takže výsledné XML použije jména použitých datových typů (což je většinou v praxi zcela nepoužitelné):
<int32>0</int32><int32>0</int32><int32>0</int32><int32>0</int32><int32>0</int32><int32>0</int32><int32>0</int32><int32>0</int32><int32>0</int32><int32>0</int32> <int32>1</int32><int32>10</int32><int32>2</int32><int32>9</int32><int32>3</int32><int32>8</int32><int32>4</int32><int32>7</int32><int32>5</int32><int32>6</int32> <string>www</string><string>root</string><string>cz</string> <int>1</int><string>root</string><float64>3.1415</float64><bool>true</bool><int>1</int><int>2</int><int>3</int><int>4</int> <float32>1</float32><float32>2</float32><float32>3</float32><float32>4</float32><float32>5</float32><float32>6</float32><float32>7</float32><float32>8</float32><float32>9</float32><float32>0</float32><float32>-1</float32><float32>0</float32>
Dále se pokusme zjistit, zda a jak vůbec je možné do XML serializovat speciální numerické hodnoty, s nimiž jsme se již seznámili v souvislosti s formátem JSON, tedy nekonečna a NaN. Kromě toho nás bude zajímat práce s ukazateli, protože právě přes ukazatele lze tvořit složitější datové struktury. Příklad nepatrně upravíme takovým způsobem, aby obsahoval lineárně vázaný seznam prvků typu Foobar:
package main import ( "encoding/xml" "fmt" "math" ) type Foobar struct { XMLName xml.Name `xml:"foobar"` Id uint32 `xml:"id"` X float64 `xml:"x"` Y float64 `xml:"y"` Z float64 `xml:"z"` Next *Foobar `xml:"foobar"` } func main() { f := Foobar{ Id: 42, X: math.NaN(), Y: math.Inf(1), Z: math.Inf(-1), Next: nil} g := Foobar{ Id: 43, X: math.NaN(), Y: math.Inf(1), Z: math.Inf(-1), Next: &f} asXML, err := xml.MarshalIndent(g, "", " ") if err != nil { fmt.Println(err) } else { fmt.Println(string(asXML)) } }
Z výstupu – serializované struktury g – je patrné, že z lineárně vázaného seznamu vznikla dvojice vnořených uzlů, což je ostatně jeden z nejlepších způsobů vizualizace této datové struktury. Dále můžeme vidět, že speciální numerické hodnoty jsou skutečně podporovány (záleží jen na kódu pro deserializaci, jak je zpracuje):
<foobar> <id>43</id> <x>NaN</x> <y>+Inf</y> <z>-Inf</z> <foobar> <id>42</id> <x>NaN</x> <y>+Inf</y> <z>-Inf</z> </foobar> </foobar>
9. Serializace sekvence struktur
Velmi často se setkáme s požadavkem na serializaci sekvence nějaké datové struktury. Představme si například seznam uživatelů. Přímé uložení pole je problematické, protože výsledek není validním XML:
package main import ( "encoding/xml" "fmt" ) type User struct { XMLName xml.Name `xml:"user"` Id uint32 `xml:"id"` Name string `xml:"user_name"` Surname string `xml:"surname"` } func main() { var users = [3]User{ User{ Id: 1, Name: "Pepek", Surname: "Vyskoč"}, User{ Id: 2, Name: "Pepek", Surname: "Vyskoč"}, User{ Id: 3, Name: "Josef", Surname: "Vyskočil"}, } usersAsXML, _ := xml.MarshalIndent(users, "", " ") fmt.Println(string(usersAsXML)) }
S výsledkem:
<user> <id>1</id> <user_name>Pepek</user_name> <surname>Vyskoč</surname> </user> <user> <id>2</id> <user_name>Pepek</user_name> <surname>Vyskoč</surname> </user> <user> <id>3</id> <user_name>Josef</user_name> <surname>Vyskočil</surname> </user>
10. Obalení sekvence struktur dalším datovým typem
V takovém případě bývá nejjednodušší obalit celou strukturu jinou strukturou, která bude obsahovat pouze specifikaci kořenového uzlu a vlastní sekvenci. Zde je navíc kořenový uzel pojmenován:
type Users struct { XMLName xml.Name `xml:"users"` List []User }
Úplný kód příkladu, v němž serializujeme několik uživatelů do formátu XML, vypadá následovně:
package main import ( "encoding/xml" "fmt" ) type User struct { Id uint32 `xml:"id"` Name string `xml:"user_name"` Surname string `xml:"surname"` } type Users struct { XMLName xml.Name `xml:"users"` List []User } func main() { var users Users = Users{ List: []User{ User{ Id: 1, Name: "Pepek", Surname: "Vyskoč"}, User{ Id: 2, Name: "Pepek", Surname: "Vyskoč"}, User{ Id: 3, Name: "Josef", Surname: "Vyskočil"}, }, } usersAsXML, _ := xml.MarshalIndent(users, "", " ") fmt.Println(string(usersAsXML)) }
Výsledek je již mnohem lepší:
<users> <List> <id>1</id> <user_name>Pepek</user_name> <surname>Vyskoč</surname> </List> <List> <id>2</id> <user_name>Pepek</user_name> <surname>Vyskoč</surname> </List> <List> <id>3</id> <user_name>Josef</user_name> <surname>Vyskočil</surname> </List> </users>
11. Vylepšení předchozích příkladů
Specifikaci jména opakujícího se uzlu (User) lze pochopitelně k této datové struktuře přidat:
type User struct { XMLName xml.Name `xml:"user"` Id uint32 `xml:"id"` Name string `xml:"user_name"` Surname string `xml:"surname"` }
Potom se kořenový uzel nezmění:
type Users struct { XMLName xml.Name `xml:"users"` List []User }
Můžeme ovšem postupovat i opačně a ponechat původní strukturu User bez uvedení jména uzlu:
type User struct { Id uint32 `xml:"id"` Name string `xml:"user_name"` Surname string `xml:"surname"` }
V tomto případě je vhodnější jméno poduzlů specifikovat v datové struktuře představující kořenový uzel:
type Users struct { XMLName xml.Name `xml:"users"` List []User `xml:"user"` }
Jen pro úplnost si obě varianty ukažme na úplném zdrojovém kódu.
První varianta:
package main import ( "encoding/xml" "fmt" ) type User struct { XMLName xml.Name `xml:"user"` Id uint32 `xml:"id"` Name string `xml:"user_name"` Surname string `xml:"surname"` } type Users struct { XMLName xml.Name `xml:"users"` List []User } func main() { var users Users = Users{ List: []User{ User{ Id: 1, Name: "Pepek", Surname: "Vyskoč"}, User{ Id: 2, Name: "Pepek", Surname: "Vyskoč"}, User{ Id: 3, Name: "Josef", Surname: "Vyskočil"}, }, } usersAsXML, _ := xml.MarshalIndent(users, "", " ") fmt.Println(string(usersAsXML)) }
Druhá varianta:
package main import ( "encoding/xml" "fmt" ) type User struct { Id uint32 `xml:"id"` Name string `xml:"user_name"` Surname string `xml:"surname"` } type Users struct { XMLName xml.Name `xml:"users"` List []User `xml:"user"` } func main() { var users Users = Users{ List: []User{ User{ Id: 1, Name: "Pepek", Surname: "Vyskoč"}, User{ Id: 2, Name: "Pepek", Surname: "Vyskoč"}, User{ Id: 3, Name: "Josef", Surname: "Vyskočil"}, }, } usersAsXML, _ := xml.MarshalIndent(users, "", " ") fmt.Println(string(usersAsXML)) }
12. Serializace jedné struktury jak do JSONu, tak i do XML – problematika anotačních řetězců
Z předchozího textu již víme, jak lze specifikovat formát uložení nějaké datové struktury do formátu JSON i do formátu XML. Ovšem v některých případech je vyžadováno, aby se stejná struktura serializovala i deserializovala do obou těchto formátů. I takového chování je pochopitelně možné docílit, a to navíc poměrně jednoduše – pouze se v anotačním řetězci specifikuje název uzlu v XML a současně i jméno atributu ve formátu JSON:
type User struct { XMLName xml.Name `xml:"user" json:"-"` Id uint32 `xml:"id" json:"user_id"` Name string `xml:"name>first" json:"user_name"` Surname string `xml:"name>last" json:"surname"` }
Úplný zdrojový kód takto upraveného demonstračního příkladu může vypadat následovně:
package main import ( "encoding/json" "encoding/xml" "fmt" ) type User struct { XMLName xml.Name `xml:"user" json:"-"` Id uint32 `xml:"id" json:"user_id"` Name string `xml:"name>first" json:"user_name"` Surname string `xml:"name>last" json:"surname"` } func main() { user := User{ Id: 1, Name: "Pepek", Surname: "Vyskoč"} userAsXML, _ := xml.MarshalIndent(user, "", " ") fmt.Println(string(userAsXML)) fmt.Println() userAsJSON, _ := json.Marshal(user) fmt.Println(string(userAsJSON)) }
Výsledky jsou očekávané:
<user> <id>1</id> <name> <first>Pepek</first> <last>Vyskoč</last> </name> </user> {"user_id":1,"user_name":"Pepek","surname":"Vyskoč"}
13. Binární formáty a programovací jazyk Go
I přesto, že se s výše uvedenými formáty JSON a XML setkáme prakticky ve všech oblastech moderního IT, nemusí se vždy jednat o nejlepší možné řešení problému přenosu strukturovaných dat. Tyto formáty totiž data neukládají v kompaktní binární podobě a navíc je parsing numerických hodnot relativně zdlouhavý, což se projevuje zejména tehdy, pokud je nutné zpracovat skutečně obrovské množství dat (buď mnoho malých zpráv či událostí, nebo naopak rozsáhlé datové soubory). A právě v těchto situacích může být výhodnější sáhnout po nějakém vhodném binárním formátu. Těch již dnes existuje velké množství, od staršího a dosti těžkopádného ASN.1 (Abstract Syntax Notation One) po formáty, které se snaží napodobit některé vlastnosti JSONu. Příkladem může být formát CBOR, jenž je podporován knihovnou https://github.com/fxamacker/cbor, popř. formát BSON, pro který pochopitelně taktéž existuje varianta pro Go. A konečně, ve světě Go se setkáme i s formátem nazvaným gob (Go Objects).
14. Formát gob
Prvním binárním formátem, s nímž se setkáme, je formát nazvaný gob neboli Go Objects. Jedná se o formát určený primárně pro použití v programovacím jazyku Go, což znamená, že jeho využití je relativně specifické (ukládání rozsáhlých dat, komunikace mezi dvojicí služeb naprogramovaných v Go atd.). Tento formát umožňuje serializaci prakticky jakékoli datové struktury, ovšem je ho možné použít i pro primitivní datové typy, resp. pro jejich hodnoty. To si ostatně ukážeme v dalším příkladu, v němž jsou serializovaná data zobrazena ve formě sekvence hexadecimálních hodnot:
package main import ( "bytes" "encoding/gob" "encoding/hex" "fmt" ) func main() { var a bool = true var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(a) if err != nil { fmt.Println(err) } else { content := buffer.Bytes() fmt.Printf("Encoded into %d bytes\n", len(content)) encoded := hex.EncodeToString(content) fmt.Println(encoded) } }
Výsledkem je:
Encoded into 4 bytes 03020001
Formát je v tomto případě jednoduchý:
- Délka dat (bez prvního bajtu)
- Typ dat
- Pozice prvku ve fiktivní struktuře (zde 0=první prvek)
- Hodnota (true se převádí na 1)
15. Serializace datové struktury do formátu gob
Do formátu gob lze uložit prakticky jakoukoli datovou strukturu, což se samozřejmě týká i uživatelsky definovaných struktur. Pokusme se tedy uložit obsah struktury typu User. V tomto případě se neuloží pouze vlastní data, ale i základní formát této struktury, což později usnadní deserializaci:
package main import ( "bytes" "encoding/gob" "encoding/hex" "fmt" ) type User struct { Id uint32 Name string Surname string } func main() { user := User{ 1, "Pepek", "Vyskoč"} var buffer bytes.Buffer encoder := gob.NewEncoder(&buffer) err := encoder.Encode(user) if err != nil { fmt.Println(err) } else { content := buffer.Bytes() fmt.Printf("Encoded into %d bytes\n", len(content)) encoded := hex.EncodeToString(content) fmt.Println(encoded) } }
Popis struktury User i její obsah se uloží do sekvence 69 bajtů, což je poměrně mnoho, ovšem na rozdíl od JSONu jsou přeneseny i datové typy apod.:
Encoded into 69 bytes 2eff81030101045573657201ff820001030102496401060001044e616d65010c0001075375726e616d65010c00000015ff8201010105506570656b01075679736b6fc48d00
16. Formát CBOR (Concise Binary Object Representation)
Jedním z binárních formátů určených pro přenos prakticky libovolně strukturovaných dat je formát nazvaný CBOR neboli plným jménem Concise Binary Object Representation. Tímto formátem, jenž se snaží nabízet podobné vlastnosti jako JSON (až na možnost jeho přímého čtení člověkem), se budeme podrobněji zabývat v navazující části tohoto seriálu, takže si prozatím jen ukažme jeden příklad, jenž používá knihovnu dostupnou na adrese github.com/fxamacker/cbor/v2 (tu lze nainstalovat běžným způsobem, ovšem pozor – vyžaduje Go 1.13 či novější). V příkladu je ukázána serializace jediné hodnoty, konkrétně pravdivostní hodnoty nastavené na hodnotu true:
package main import ( "fmt" "github.com/fxamacker/cbor/v2" ) func main() { var a bool = true var jsonOutput []byte cborOutput, _ = cbor.Marshal(a) fmt.Println(string(cborOutput)) }
17. Serializace dat do formátu BSON
Dalším sice relativně novým, ale rozšiřujícím se binárním formátem je formát nazvaný BSON (zde je odkaz na JSON nesporný). Tento formát je podporován v knihovně dodávané společně s ovladači pro MongoDB, ale lze ho používat zcela samostatně a nezávisle. Rozhraní balíčku bson je totožné s rozhraním encoding/json, takže například serializace naší struktury User do souboru typu BSON může vypadat následovně:
package main import ( "fmt" "gopkg.in/mgo.v2/bson" "io/ioutil" ) type User struct { Id uint32 Name string Surname string } func main() { user := User{ 1, "Pepek", "Vyskoč"} var bsonOutput []byte bsonOutput, err := bson.Marshal(user) if err != nil { fmt.Println(err) } else { fmt.Printf("Encoded into %d bytes\n", len(bsonOutput)) err := ioutil.WriteFile("1.bson", bsonOutput, 0644) if err != nil { fmt.Println(err) } else { fmt.Println("And stored into file") } } }
18. Deserializace dat z formátu BSON
V předchozím příkladu jsme si ukázali serializaci datové struktury do BSONu, takže nám logicky zbývá provést její zpětnou deserializaci. Postup je velmi jednoduchý a je ukázán v dnešním posledním příkladu. Prozatím si ukazujeme pouze „happy path“, tj. situaci, kdy je deserializace úspěšná, ale příště se budeme zabývat i složitějšími stavy, které mohou v praxi nastat:
package main import ( "fmt" "gopkg.in/mgo.v2/bson" "io/ioutil" ) type User struct { Id uint32 Name string Surname string } func main() { var user User bsonInput, err := ioutil.ReadFile("1.bson") if err != nil { fmt.Println(err) return } fmt.Printf("Read %d bytes\n", len(bsonInput)) err = bson.Unmarshal(bsonInput, &user) if err != nil { fmt.Println(err) return } fmt.Println("Deserialized value") fmt.Println(user) }
19. Repositář s demonstračními příklady
Zdrojové kódy všech dnes použitých demonstračních příkladů byly uloženy do Git repositáře, který je dostupný na adrese https://github.com/tisnik/go-root (stále na GitHubu :-). V případě, že nebudete chtít klonovat celý repositář (ten je ovšem – alespoň prozatím – velmi malý, dnes má přibližně šest až sedm megabajtů), můžete namísto toho použít odkazy na jednotlivé demonstrační příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- Gobs of data
https://blog.golang.org/gobs-of-data - Formát BSON
http://bsonspec.org/ - Golang Guide: A List of Top Golang Frameworks, IDEs & Tools
https://blog.intelligentbee.com/2017/08/14/golang-guide-list-top-golang-frameworks-ides-tools/ - Repositář projektu Gift
https://github.com/disintegration/gift - Dokumentace k projektu Gift
https://godoc.org/github.com/disintegration/gift - Online x86 / x64 Assembler and Disassembler
https://defuse.ca/online-x86-assembler.htm#disassembly2 - The Design of the Go Assembler
https://talks.golang.org/2016/asm.slide#1 - A Quick Guide to Go's Assembler
https://golang.org/doc/asm - AssemblyPolicy
https://github.com/golang/go/wiki/AssemblyPolicy - Geohash in Golang Assembly
https://mmcloughlin.com/posts/geohash-assembly - Command objdump
https://golang.org/cmd/objdump/ - Assembly
https://goroutines.com/asm - Go & Assembly
http://www.doxsey.net/blog/go-and-assembly - A Foray Into Go Assembly Programming
https://blog.sgmansfield.com/2017/04/a-foray-into-go-assembly-programming/ - Golang Capturing log.Println And fmt.Println Output
https://medium.com/@hau12a1/golang-capturing-log-println-and-fmt-println-output-770209c791b4 - Stránka projektu plotly
https://plot.ly/ - Plotly JavaScript Open Source Graphing Library
https://plot.ly/javascript/ - Domain coloring
https://en.wikipedia.org/wiki/Domain_coloring - Michael Fogleman's projects
https://www.michaelfogleman.com/projects/tagged/graphics/ - Color Graphs of Complex Functions
https://web.archive.org/web/20120511021419/http://w.american.edu/cas/mathstat/lcrone/ComplexPlot.html - A Gallery of Complex Functions
http://wismuth.com/complex/gallery.html - package glot
https://godoc.org/github.com/Arafatk/glot - Gnuplotting: Output terminals
http://www.gnuplotting.org/output-terminals/ - Introducing Glot the plotting library for Golang
https://medium.com/@Arafat./introducing-glot-the-plotting-library-for-golang-3133399948a1 - Introducing Glot the plotting library for Golang
https://blog.gopheracademy.com/advent-2018/introducing-glot/ - Glot is a plotting library for Golang built on top of gnuplot
https://github.com/Arafatk/glot - Example plots (gonum/plot)
https://github.com/gonum/plot/wiki/Example-plots - A repository for plotting and visualizing data (gonum/plot)
https://github.com/gonum/plot - golang library to make https://chartjs.org/ plots
https://github.com/brentp/go-chartjs - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - The Gonum Numerical Computing Package
https://www.gonum.org/post/introtogonum/ - Gomacro na GitHubu
https://github.com/cosmos72/gomacro - gophernotes – Use Go in Jupyter notebooks and nteract
https://github.com/gopherdata/gophernotes - gonum
https://github.com/gonum - go-gota/gota – DataFrames and data wrangling in Go (Golang)
https://porter.io/github.com/go-gota/gota - A repository for plotting and visualizing data
https://github.com/gonum/plot - Gonum Numerical Packages
https://www.gonum.org/ - Stránky projektu MinIO
https://min.io/ - MinIO Quickstart Guide
https://docs.min.io/docs/minio-quickstart-guide.html - MinIO Go Client API Reference
https://docs.min.io/docs/golang-client-api-reference - MinIO Python Client API Reference
https://docs.min.io/docs/python-client-api-reference.html - Performance at Scale: MinIO Pushes Past 1.4 terabits per second with 256 NVMe Drives
https://blog.min.io/performance-at-scale-minio-pushes-past-1–3-terabits-per-second-with-256-nvme-drives/ - Benchmarking MinIO vs. AWS S3 for Apache Spark
https://blog.min.io/benchmarking-apache-spark-vs-aws-s3/ - MinIO Client Quickstart Guide
https://docs.min.io/docs/minio-client-quickstart-guide.html - Analýza kvality zdrojových kódů Minia
https://goreportcard.com/report/github.com/minio/minio - This is MinIO
https://www.youtube.com/watch?v=vF0lQh0XOCs - Running MinIO Standalone
https://www.youtube.com/watch?v=dIQsPCHvHoM - „Amazon S3 Compatible Storage in Kubernetes“ – Rob Girard, Principal Tech Marketing Engineer, Minio
https://www.youtube.com/watch?v=wlpn8K0jJ4U - Ginkgo
http://onsi.github.io/ginkgo/ - Gomega
https://onsi.github.io/gomega/ - Ginkgo's Preferred Matcher Library na GitHubu
https://github.com/onsi/gomega/ - Provided Matchers
http://onsi.github.io/gomega/#provided-matchers - Dokumentace k balíčku goexpect
https://godoc.org/github.com/google/goexpect - Balíček goexpect
https://github.com/google/goexpect - Balíček go-expect
https://github.com/Netflix/go-expect - Balíček gexpect
https://github.com/ThomasRooney/gexpect - Expect (originál naprogramovaný v TCL)
https://core.tcl-lang.org/expect/index - Expect (Wikipedia)
https://en.wikipedia.org/wiki/Expect - Pexpect
https://pexpect.readthedocs.io/en/stable/ - Golang SSH Client: Multiple Commands, Crypto & Goexpect Examples
http://networkbit.ch/golang-ssh-client/ - goblin na GitHubu
https://github.com/franela/goblin - Mocha framework
https://mochajs.org/ - frisby na GitHubu
https://github.com/verdverm/frisby - package frisby
https://godoc.org/github.com/verdverm/frisby - Frisby alternatives and similar packages (generováno)
https://go.libhunt.com/frisby-alternatives - Cucumber for golang
https://github.com/DATA-DOG/godog - How to Use Godog for Behavior-driven Development in Go
https://semaphoreci.com/community/tutorials/how-to-use-godog-for-behavior-driven-development-in-go - Comparative Analysis Of GoLang Testing Frameworks
https://www.slideshare.net/DushyantBhalgami/comparative-analysis-of-golang-testing-frameworks - A Quick Guide to Testing in Golang
https://caitiem.com/2016/08/18/a-quick-guide-to-testing-in-golang/ - Tom's Obvious, Minimal Language.
https://github.com/toml-lang/toml - xml.org
http://www.xml.org/ - Soubory .properties
https://en.wikipedia.org/wiki/.properties - Soubory INI
https://en.wikipedia.org/wiki/INI_file - JSON to YAML
https://www.json2yaml.com/ - Data Format Converter
https://toolkit.site/format.html - Viper na GitHubu
https://github.com/spf13/viper - GoDotEnv na GitHubu
https://github.com/joho/godotenv - The fantastic ORM library for Golang
http://gorm.io/ - Dokumentace k balíčku gorilla/mux
https://godoc.org/github.com/gorilla/mux - Gorilla web toolkitk
http://www.gorillatoolkit.org/ - Metric types
https://prometheus.io/docs/concepts/metric_types/ - Histograms with Prometheus: A Tale of Woe
http://linuxczar.net/blog/2017/06/15/prometheus-histogram-2/ - Why are Prometheus histograms cumulative?
https://www.robustperception.io/why-are-prometheus-histograms-cumulative - Histograms and summaries
https://prometheus.io/docs/practices/histograms/ - Instrumenting Golang server in 5 min
https://medium.com/@gsisimogang/instrumenting-golang-server-in-5-min-c1c32489add3 - Semantic Import Versioning in Go
https://www.aaronzhuo.com/semantic-import-versioning-in-go/ - Sémantické verzování
https://semver.org/ - Getting started with Go modules
https://medium.com/@fonseka.live/getting-started-with-go-modules-b3dac652066d - Create projects independent of $GOPATH using Go Modules
https://medium.com/mindorks/create-projects-independent-of-gopath-using-go-modules-802260cdfb51o - Anatomy of Modules in Go
https://medium.com/rungo/anatomy-of-modules-in-go-c8274d215c16 - Modules
https://github.com/golang/go/wiki/Modules - Go Modules Tutorial
https://tutorialedge.net/golang/go-modules-tutorial/ - Module support
https://golang.org/cmd/go/#hdr-Module_support - Go Lang: Memory Management and Garbage Collection
https://vikash1976.wordpress.com/2017/03/26/go-lang-memory-management-and-garbage-collection/ - Golang Internals, Part 4: Object Files and Function Metadata
https://blog.altoros.com/golang-part-4-object-files-and-function-metadata.html - What is REPL?
https://pythonprogramminglanguage.com/repl/ - What is a REPL?
https://codewith.mu/en/tutorials/1.0/repl - Programming at the REPL: Introduction
https://clojure.org/guides/repl/introduction - What is REPL? (Quora)
https://www.quora.com/What-is-REPL - Gorilla REPL: interaktivní prostředí pro programovací jazyk Clojure
https://www.root.cz/clanky/gorilla-repl-interaktivni-prostredi-pro-programovaci-jazyk-clojure/ - Read-eval-print loop (Wikipedia)
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - Vim as a Go (Golang) IDE using LSP and vim-go
https://octetz.com/posts/vim-as-go-ide - gopls
https://github.com/golang/go/wiki/gopls - IDE Integration Guide
https://github.com/stamblerre/gocode/blob/master/docs/IDE_integration.md - How to instrument Go code with custom expvar metrics
https://sysdig.com/blog/golang-expvar-custom-metrics/ - Golang expvar metricset (Metricbeat Reference)
https://www.elastic.co/guide/en/beats/metricbeat/7.x/metricbeat-metricset-golang-expvar.html - Package expvar
https://golang.org/pkg/expvar/#NewInt - Java Platform Debugger Architecture: Overview
https://docs.oracle.com/en/java/javase/11/docs/specs/jpda/jpda.html - The JVM Tool Interface (JVM TI): How VM Agents Work
https://www.oracle.com/technetwork/articles/javase/index-140680.html - JVM Tool Interface Version 11.0
https://docs.oracle.com/en/java/javase/11/docs/specs/jvmti.html - Creating a Debugging and Profiling Agent with JVMTI
http://www.oracle.com/technetwork/articles/javase/jvmti-136367.html - JVM TI (Wikipedia)
http://en.wikipedia.org/wiki/JVM_TI - IBM JVMTI extensions
http://publib.boulder.ibm.com/infocenter/realtime/v2r0/index.jsp?topic=%2Fcom.ibm.softrt.doc%2Fdiag%2Ftools%2Fjvmti_extensions.html - Go & cgo: integrating existing C code with Go
http://akrennmair.github.io/golang-cgo-slides/#1 - Using cgo to call C code from within Go code
https://wenzr.wordpress.com/2018/06/07/using-cgo-to-call-c-code-from-within-go-code/ - Package trace
https://golang.org/pkg/runtime/trace/ - Introducing HTTP Tracing
https://blog.golang.org/http-tracing - Command trace
https://golang.org/cmd/trace/ - A StreamLike, Immutable, Lazy Loading and smart Golang Library to deal with slices
https://github.com/wesovilabs/koazee - Funkce vyššího řádu v knihovně Underscore
https://www.root.cz/clanky/funkce-vyssiho-radu-v-knihovne-underscore/ - Delve: a debugger for the Go programming language.
https://github.com/go-delve/delve - Příkazy debuggeru Delve
https://github.com/go-delve/delve/tree/master/Documentation/cli - Debuggery a jejich nadstavby v Linuxu
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu/ - Debuggery a jejich nadstavby v Linuxu (2. část)
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-2-cast/ - Debuggery a jejich nadstavby v Linuxu (3): Nemiver
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-3-nemiver/ - Debuggery a jejich nadstavby v Linuxu (4): KDbg
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-4-kdbg/ - Debuggery a jejich nadstavby v Linuxu (5): ladění aplikací v editorech Emacs a Vim
http://mojefedora.cz/debuggery-a-jejich-nadstavby-v-linuxu-5-ladeni-aplikaci-v-editorech-emacs-a-vim/ - Debugging Go Code with GDB
https://golang.org/doc/gdb - Debugging Go (golang) programs with gdb
https://thornydev.blogspot.com/2014/01/debugging-go-golang-programs-with-gdb.html - GDB – Dokumentace
http://sourceware.org/gdb/current/onlinedocs/gdb/ - GDB – Supported Languages
http://sourceware.org/gdb/current/onlinedocs/gdb/Supported-Languages.html#Supported-Languages - GNU Debugger (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Debugger - The LLDB Debugger
http://lldb.llvm.org/ - Debugger (Wikipedia)
https://en.wikipedia.org/wiki/Debugger - 13 Linux Debuggers for C++ Reviewed
http://www.drdobbs.com/testing/13-linux-debuggers-for-c-reviewed/240156817 - Go is on a Trajectory to Become the Next Enterprise Programming Language
https://hackernoon.com/go-is-on-a-trajectory-to-become-the-next-enterprise-programming-language-3b75d70544e - Go Proverbs: Simple, Poetic, Pithy
https://go-proverbs.github.io/ - Handling Sparse Files on Linux
https://www.systutorials.com/136652/handling-sparse-files-on-linux/ - Gzip (Wikipedia)
https://en.wikipedia.org/wiki/Gzip - Deflate
https://en.wikipedia.org/wiki/DEFLATE - 10 tools written in Go that every developer needs to know
https://gustavohenrique.net/en/2019/01/10-tools-written-in-go-that-every-dev-needs-to-know/ - Hexadecimální prohlížeče a editory s textovým uživatelským rozhraním
https://www.root.cz/clanky/hexadecimalni-prohlizece-a-editory-s-textovym-uzivatelskym-rozhranim/ - Hex dump
https://en.wikipedia.org/wiki/Hex_dump - Rozhraní io.ByteReader
https://golang.org/pkg/io/#ByteReader - Rozhraní io.RuneReader
https://golang.org/pkg/io/#RuneReader - Rozhraní io.ByteScanner
https://golang.org/pkg/io/#ByteScanner - Rozhraní io.RuneScanner
https://golang.org/pkg/io/#RuneScanner - Rozhraní io.Closer
https://golang.org/pkg/io/#Closer - Rozhraní io.Reader
https://golang.org/pkg/io/#Reader - Rozhraní io.Writer
https://golang.org/pkg/io/#Writer - Typ Strings.Reader
https://golang.org/pkg/strings/#Reader - VACUUM (SQL)
https://www.sqlite.org/lang_vacuum.html - VACUUM (Postgres)
https://www.postgresql.org/docs/8.4/sql-vacuum.html - go-cron
https://github.com/rk/go-cron - gocron
https://github.com/jasonlvhit/gocron - clockwork
https://github.com/whiteShtef/clockwork - clockwerk
https://github.com/onatm/clockwerk - JobRunner
https://github.com/bamzi/jobrunner - Rethinking Cron
https://adam.herokuapp.com/past/2010/4/13/rethinking_cron/ - In the Beginning was the Command Line
https://web.archive.org/web/20180218045352/http://www.cryptonomicon.com/beginning.html - repl.it (REPL pro různé jazyky)
https://repl.it/languages - GOCUI – Go Console User Interface (celé uživatelské prostředí, nejenom input box)
https://github.com/jroimartin/gocui - Read–eval–print loop
https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop - go-prompt
https://github.com/c-bata/go-prompt - readline
https://github.com/chzyer/readline - A pure golang implementation for GNU-Readline kind library
https://golangexample.com/a-pure-golang-implementation-for-gnu-readline-kind-library/ - go-readline
https://github.com/fiorix/go-readline - 4 Python libraries for building great command-line user interfaces
https://opensource.com/article/17/5/4-practical-python-libraries - prompt_toolkit 2.0.3 na PyPi
https://pypi.org/project/prompt_toolkit/ - python-prompt-toolkit na GitHubu
https://github.com/jonathanslenders/python-prompt-toolkit - The GNU Readline Library
https://tiswww.case.edu/php/chet/readline/rltop.html - GNU Readline (Wikipedia)
https://en.wikipedia.org/wiki/GNU_Readline - readline — GNU readline interface (Python 3.x)
https://docs.python.org/3/library/readline.html - readline — GNU readline interface (Python 2.x)
https://docs.python.org/2/library/readline.html - GNU Readline Library – command line editing
https://tiswww.cwru.edu/php/chet/readline/readline.html - gnureadline 6.3.8 na PyPi
https://pypi.org/project/gnureadline/ - Editline Library (libedit)
http://thrysoee.dk/editline/ - Comparing Python Command-Line Parsing Libraries – Argparse, Docopt, and Click
https://realpython.com/comparing-python-command-line-parsing-libraries-argparse-docopt-click/ - libedit or editline
http://www.cs.utah.edu/~bigler/code/libedit.html - WinEditLine
http://mingweditline.sourceforge.net/ - rlcompleter — Completion function for GNU readline
https://docs.python.org/3/library/rlcompleter.html - rlwrap na GitHubu
https://github.com/hanslub42/rlwrap - rlwrap(1) – Linux man page
https://linux.die.net/man/1/rlwrap - readline(3) – Linux man page
https://linux.die.net/man/3/readline - history(3) – Linux man page
https://linux.die.net/man/3/history - Dokumentace k balíčku oglematchers
https://godoc.org/github.com/jacobsa/oglematchers - Balíček oglematchers
https://github.com/jacobsa/oglematchers - Dokumentace k balíčku ogletest
https://godoc.org/github.com/jacobsa/ogletest - Balíček ogletest
https://github.com/jacobsa/ogletest - Dokumentace k balíčku assert
https://godoc.org/github.com/stretchr/testify/assert - Testify – Thou Shalt Write Tests
https://github.com/stretchr/testify/ - package testing
https://golang.org/pkg/testing/ - Golang basics – writing unit tests
https://blog.alexellis.io/golang-writing-unit-tests/ - An Introduction to Programming in Go / Testing
https://www.golang-book.com/books/intro/12 - An Introduction to Testing in Go
https://tutorialedge.net/golang/intro-testing-in-go/ - Advanced Go Testing Tutorial
https://tutorialedge.net/golang/advanced-go-testing-tutorial/ - GoConvey
http://goconvey.co/ - Testing Techniques
https://talks.golang.org/2014/testing.slide - 5 simple tips and tricks for writing unit tests in #golang
https://medium.com/@matryer/5-simple-tips-and-tricks-for-writing-unit-tests-in-golang-619653f90742 - Afinní transformace
https://cs.wikibooks.org/wiki/Geometrie/Afinn%C3%AD_transformace_sou%C5%99adnic - package gg
https://godoc.org/github.com/fogleman/gg - Generate an animated GIF with Golang
http://tech.nitoyon.com/en/blog/2016/01/07/go-animated-gif-gen/ - Generate an image programmatically with Golang
http://tech.nitoyon.com/en/blog/2015/12/31/go-image-gen/ - The Go image package
https://blog.golang.org/go-image-package - Balíček draw2D: 2D rendering for different output (raster, pdf, svg)
https://github.com/llgcode/draw2d - Draw a rectangle in Golang?
https://stackoverflow.com/questions/28992396/draw-a-rectangle-in-golang - YAML
https://yaml.org/ - edn
https://github.com/edn-format/edn - Smile
https://github.com/FasterXML/smile-format-specification - Protocol-Buffers
https://developers.google.com/protocol-buffers/ - Marshalling (computer science)
https://en.wikipedia.org/wiki/Marshalling_(computer_science) - Unmarshalling
https://en.wikipedia.org/wiki/Unmarshalling - Introducing JSON
http://json.org/ - Package json
https://golang.org/pkg/encoding/json/ - The Go Blog: JSON and Go
https://blog.golang.org/json-and-go - Go by Example: JSON
https://gobyexample.com/json - Writing Web Applications
https://golang.org/doc/articles/wiki/ - Golang Web Apps
https://www.reinbach.com/blog/golang-webapps-1/ - Build web application with Golang
https://legacy.gitbook.com/book/astaxie/build-web-application-with-golang/details - Golang Templates – Golang Web Pages
https://www.youtube.com/watch?v=TkNIETmF-RU - Simple Golang HTTPS/TLS Examples
https://github.com/denji/golang-tls - Playing with images in HTTP response in golang
https://www.sanarias.com/blog/1214PlayingwithimagesinHTTPresponseingolang - MIME Types List
https://www.freeformatter.com/mime-types-list.html - Go Mutex Tutorial
https://tutorialedge.net/golang/go-mutex-tutorial/ - Creating A Simple Web Server With Golang
https://tutorialedge.net/golang/creating-simple-web-server-with-golang/ - Building a Web Server in Go
https://thenewstack.io/building-a-web-server-in-go/ - How big is the pipe buffer?
https://unix.stackexchange.com/questions/11946/how-big-is-the-pipe-buffer - How to turn off buffering of stdout in C
https://stackoverflow.com/questions/7876660/how-to-turn-off-buffering-of-stdout-in-c - setbuf(3) – Linux man page
https://linux.die.net/man/3/setbuf - setvbuf(3) – Linux man page (stejný obsah jako předchozí stránka)
https://linux.die.net/man/3/setvbuf - Select waits on a group of channels
https://yourbasic.org/golang/select-explained/ - Rob Pike: Simplicity is Complicated (video)
http://www.golang.to/posts/dotgo-2015-rob-pike-simplicity-is-complicated-youtube-16893 - Algorithms to Go
https://yourbasic.org/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu/ - Využití knihovny Pygments (nejenom) pro obarvení zdrojových kódů: vlastní filtry a lexery
https://www.root.cz/clanky/vyuziti-knihovny-pygments-nejenom-pro-obarveni-zdrojovych-kodu-vlastni-filtry-a-lexery/ - Go Defer Simplified with Practical Visuals
https://blog.learngoprogramming.com/golang-defer-simplified-77d3b2b817ff - 5 More Gotchas of Defer in Go — Part II
https://blog.learngoprogramming.com/5-gotchas-of-defer-in-go-golang-part-ii-cc550f6ad9aa - The Go Blog: Defer, Panic, and Recover
https://blog.golang.org/defer-panic-and-recover - The defer keyword in Swift 2: try/finally done right
https://www.hackingwithswift.com/new-syntax-swift-2-defer - Swift Defer Statement
https://andybargh.com/swift-defer-statement/ - Modulo operation (Wikipedia)
https://en.wikipedia.org/wiki/Modulo_operation - Node.js vs Golang: Battle of the Next-Gen Languages
https://www.hostingadvice.com/blog/nodejs-vs-golang/ - The Go Programming Language (home page)
https://golang.org/ - GoDoc
https://godoc.org/ - Go (programming language), Wikipedia
https://en.wikipedia.org/wiki/Go_(programming_language) - Go Books (kniha o jazyku Go)
https://github.com/dariubs/GoBooks - The Go Programming Language Specification
https://golang.org/ref/spec - Go: the Good, the Bad and the Ugly
https://bluxte.net/musings/2018/04/10/go-good-bad-ugly/ - Package builtin
https://golang.org/pkg/builtin/ - Package fmt
https://golang.org/pkg/fmt/ - The Little Go Book (další kniha)
https://github.com/dariubs/GoBooks - The Go Programming Language by Brian W. Kernighan, Alan A. A. Donovan
https://www.safaribooksonline.com/library/view/the-go-programming/9780134190570/ebook_split010.html - Learning Go
https://www.miek.nl/go/ - Go Bootcamp
http://www.golangbootcamp.com/ - Programming in Go: Creating Applications for the 21st Century (další kniha o jazyku Go)
http://www.informit.com/store/programming-in-go-creating-applications-for-the-21st-9780321774637 - Introducing Go (Build Reliable, Scalable Programs)
http://shop.oreilly.com/product/0636920046516.do - Learning Go Programming
https://www.packtpub.com/application-development/learning-go-programming - The Go Blog
https://blog.golang.org/ - Getting to Go: The Journey of Go's Garbage Collector
https://blog.golang.org/ismmkeynote - Go (programovací jazyk, Wikipedia)
https://cs.wikipedia.org/wiki/Go_(programovac%C3%AD_jazyk) - Rychle, rychleji až úplně nejrychleji s jazykem Go
https://www.root.cz/clanky/rychle-rychleji-az-uplne-nejrychleji-s-jazykem-go/ - Installing Go on the Raspberry Pi
https://dave.cheney.net/2012/09/25/installing-go-on-the-raspberry-pi - How the Go runtime implements maps efficiently (without generics)
https://dave.cheney.net/2018/05/29/how-the-go-runtime-implements-maps-efficiently-without-generics - Niečo málo o Go – Golang (slovensky)
http://golangsk.logdown.com/ - How Many Go Developers Are There?
https://research.swtch.com/gophercount - Most Popular Technologies (Stack Overflow Survery 2018)
https://insights.stackoverflow.com/survey/2018/#most-popular-technologies - Most Popular Technologies (Stack Overflow Survery 2017)
https://insights.stackoverflow.com/survey/2017#technology - JavaScript vs. Golang for IoT: Is Gopher Winning?
https://www.iotforall.com/javascript-vs-golang-iot/ - The Go Programming Language: Release History
https://golang.org/doc/devel/release.html - Go 1.11 Release Notes
https://golang.org/doc/go1.11 - Go 1.10 Release Notes
https://golang.org/doc/go1.10 - Go 1.9 Release Notes (tato verze je stále používána)
https://golang.org/doc/go1.9 - Go 1.8 Release Notes (i tato verze je stále používána)
https://golang.org/doc/go1.8 - Go on Fedora
https://developer.fedoraproject.org/tech/languages/go/go-installation.html - Writing Go programs
https://developer.fedoraproject.org/tech/languages/go/go-programs.html - The GOPATH environment variable
https://tip.golang.org/doc/code.html#GOPATH - Command gofmt
https://tip.golang.org/cmd/gofmt/ - The Go Blog: go fmt your code
https://blog.golang.org/go-fmt-your-code - C? Go? Cgo!
https://blog.golang.org/c-go-cgo - Spaces vs. Tabs: A 20-Year Debate Reignited by Google’s Golang
https://thenewstack.io/spaces-vs-tabs-a-20-year-debate-and-now-this-what-the-hell-is-wrong-with-go/ - 400,000 GitHub repositories, 1 billion files, 14 terabytes of code: Spaces or Tabs?
https://medium.com/@hoffa/400–000-github-repositories-1-billion-files-14-terabytes-of-code-spaces-or-tabs-7cfe0b5dd7fd - Gofmt No Longer Allows Spaces. Tabs Only
https://news.ycombinator.com/item?id=7914523 - Why does Go „go fmt“ uses tabs instead of whitespaces?
https://www.quora.com/Why-does-Go-go-fmt-uses-tabs-instead-of-whitespaces - Interactive: The Top Programming Languages 2018
https://spectrum.ieee.org/static/interactive-the-top-programming-languages-2018 - Go vs. Python
https://www.peterbe.com/plog/govspy - PackageManagementTools
https://github.com/golang/go/wiki/PackageManagementTools - A Tour of Go: Type inference
https://tour.golang.org/basics/14 - Go Slices: usage and internals
https://blog.golang.org/go-slices-usage-and-internals - Go by Example: Slices
https://gobyexample.com/slices - What is the point of slice type in Go?
https://stackoverflow.com/questions/2098874/what-is-the-point-of-slice-type-in-go - The curious case of Golang array and slices
https://medium.com/@hackintoshrao/the-curious-case-of-golang-array-and-slices-2565491d4335 - Introduction to Slices in Golang
https://www.callicoder.com/golang-slices/ - Golang: Understanding ‚null‘ and nil
https://newfivefour.com/golang-null-nil.html - What does nil mean in golang?
https://stackoverflow.com/questions/35983118/what-does-nil-mean-in-golang - nils In Go
https://go101.org/article/nil.html - Go slices are not dynamic arrays
https://appliedgo.net/slices/ - Go-is-no-good (nelze brát doslova)
https://github.com/ksimka/go-is-not-good - Rust vs. Go
https://news.ycombinator.com/item?id=13430108 - Seriál Programovací jazyk Rust
https://www.root.cz/serialy/programovaci-jazyk-rust/ - Modern garbage collection: A look at the Go GC strategy
https://blog.plan99.net/modern-garbage-collection-911ef4f8bd8e - Go GC: Prioritizing low latency and simplicity
https://blog.golang.org/go15gc - Is Golang a good language for embedded systems?
https://www.quora.com/Is-Golang-a-good-language-for-embedded-systems - Running GoLang on an STM32 MCU. A quick tutorial.
https://www.mickmake.com/post/running-golang-on-an-mcu-a-quick-tutorial - Go, Robot, Go! Golang Powered Robotics
https://gobot.io/ - Emgo: Bare metal Go (language for programming embedded systems)
https://github.com/ziutek/emgo - UTF-8 history
https://www.cl.cam.ac.uk/~mgk25/ucs/utf-8-history.txt - Less is exponentially more
https://commandcenter.blogspot.com/2012/06/less-is-exponentially-more.html - Should I Rust, or Should I Go
https://codeburst.io/should-i-rust-or-should-i-go-59a298e00ea9 - Setting up and using gccgo
https://golang.org/doc/install/gccgo - Elastic Tabstops
http://nickgravgaard.com/elastic-tabstops/ - Strings, bytes, runes and characters in Go
https://blog.golang.org/strings - Datový typ
https://cs.wikipedia.org/wiki/Datov%C3%BD_typ - Seriál o programovacím jazyku Rust: Základní (primitivní) datové typy
https://www.root.cz/clanky/programovaci-jazyk-rust-nahrada-c-nebo-slepa-cesta/#k09 - Seriál o programovacím jazyku Rust: Vytvoření „řezu“ z pole
https://www.root.cz/clanky/prace-s-poli-v-programovacim-jazyku-rust/#k06 - Seriál o programovacím jazyku Rust: Řezy (slice) vektoru
https://www.root.cz/clanky/prace-s-vektory-v-programovacim-jazyku-rust/#k05 - Printf Format Strings
https://www.cprogramming.com/tutorial/printf-format-strings.html - Java: String.format
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#format-java.lang.String-java.lang.Object…- - Java: format string syntax
https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html#syntax - Selectors
https://golang.org/ref/spec#Selectors - Calling Go code from Python code
http://savorywatt.com/2015/09/18/calling-go-code-from-python-code/ - Go Data Structures: Interfaces
https://research.swtch.com/interfaces - How to use interfaces in Go
http://jordanorelli.com/post/32665860244/how-to-use-interfaces-in-go - Interfaces in Go (part I)
https://medium.com/golangspec/interfaces-in-go-part-i-4ae53a97479c - Part 21: Goroutines
https://golangbot.com/goroutines/ - Part 22: Channels
https://golangbot.com/channels/ - [Go] Lightweight eventbus with async compatibility for Go
https://github.com/asaskevich/EventBus - What about Trait support in Golang?
https://www.reddit.com/r/golang/comments/8mfykl/what_about_trait_support_in_golang/ - Don't Get Bitten by Pointer vs Non-Pointer Method Receivers in Golang
https://nathanleclaire.com/blog/2014/08/09/dont-get-bitten-by-pointer-vs-non-pointer-method-receivers-in-golang/ - Control Flow
https://en.wikipedia.org/wiki/Control_flow - Structured programming
https://en.wikipedia.org/wiki/Structured_programming - Control Structures
https://www.golang-book.com/books/intro/5 - Control structures – Go if else statement
http://golangtutorials.blogspot.com/2011/06/control-structures-if-else-statement.html - Control structures – Go switch case statement
http://golangtutorials.blogspot.com/2011/06/control-structures-go-switch-case.html - Control structures – Go for loop, break, continue, range
http://golangtutorials.blogspot.com/2011/06/control-structures-go-for-loop-break.html - Goroutine IDs
https://blog.sgmansfield.com/2015/12/goroutine-ids/ - Different ways to pass channels as arguments in function in go (golang)
https://stackoverflow.com/questions/24868859/different-ways-to-pass-channels-as-arguments-in-function-in-go-golang - justforfunc #22: using the Go execution tracer
https://www.youtube.com/watch?v=ySy3sR1LFCQ - Single Function Exit Point
http://wiki.c2.com/?SingleFunctionExitPoint - Entry point
https://en.wikipedia.org/wiki/Entry_point - Why does Go have a GOTO statement?!
https://www.reddit.com/r/golang/comments/kag5q/why_does_go_have_a_goto_statement/ - Effective Go
https://golang.org/doc/effective_go.html - GoClipse: an Eclipse IDE for the Go programming language
http://goclipse.github.io/ - GoClipse Installation
https://github.com/GoClipse/goclipse/blob/latest/documentation/Installation.md#installation - The zero value of a slice is not nil
https://stackoverflow.com/questions/30806931/the-zero-value-of-a-slice-is-not-nil - Go-tcha: When nil != nil
https://dev.to/pauljlucas/go-tcha-when-nil–nil-hic - Nils in Go
https://www.doxsey.net/blog/nils-in-go