Obsah
1. Zpracování JSON formátu v programovacím jazyce Go
2. „Marshalling“ datových struktur do formátu JSON
3. Převod celočíselných hodnot do JSONu
4. Chování systému při pokusu o převod hodnot, které nemají podporu v JSONu
5. Převod polí a řezů do JSONu
6. Struktury (záznamy) a jejich přímý převod do JSONu
7. Jedno z nejčastějších použití: mapy struktur (záznamů)
8. Složitější (vnořené) datové struktury, změna názvů klíčů ve výsledném JSONu
9. Export speciálních hodnot do JSONu
10. Import dat ve formátu JSON
11. Import jednoduché struktury (záznamu) se známým obsahem
13. Import map obsahujících struktury (záznamy)
14. Specifikace klíčů zapsaných v souborech JSON
15. Načtení předem neznámé struktury z JSONu
16. Práce s rastrovými obrázky
17. Export rastrového obrazu do PNG
18. Export rastrového obrazu do formátu JPEG
19. Repositář s demonstračními příklady
1. Zpracování JSON formátu v programovacím jazyce Go
V první části dnešního článku se zaměříme na popis způsobů použití formátu JSON v aplikacích vyvinutých v programovacím jazyku Go. Jedná se o poměrně důležitou oblast, 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), je velmi pravděpodobné, že se JSON bude i nadále poměrně masivně využívat. Formát JSON tak, jak ho budeme zpracovávat v dnešních demonstračních příkladech, je stručně a přitom dostatečně přesně popsán na známé a často navštěvované stránce http://json.org/.
Příklady reálných dat uložených ve formátu JSON není těžké získat. Ukážeme si pouze tři příklady získané z veřejně dostupných webových služeb.
Využití služby, která vrátí hlavičky posílané klientem:
$ curl http://httpbin.org/get { "args": {}, "headers": { "Accept": "*/*", "Host": "httpbin.org", "User-Agent": "curl/7.35.0" }, "origin": "89.24.49.226, 89.24.49.226", "url": "https://httpbin.org/get" }
Služba vracející dokument reprezentovaný ve formátu JSON:
$ 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" } }
Veřejně dostupné REST API nalezneme i v případě GitHubu:
$ curl https://api.github.com/users/torvalds { "login": "torvalds", "id": 1024025, "node_id": "MDQ6VXNlcjEwMjQwMjU=", "avatar_url": "https://avatars0.githubusercontent.com/u/1024025?v=4", "gravatar_id": "", "url": "https://api.github.com/users/torvalds", "html_url": "https://github.com/torvalds", "followers_url": "https://api.github.com/users/torvalds/followers", "following_url": "https://api.github.com/users/torvalds/following{/other_user}", "gists_url": "https://api.github.com/users/torvalds/gists{/gist_id}", "starred_url": "https://api.github.com/users/torvalds/starred{/owner}{/repo}", "subscriptions_url": "https://api.github.com/users/torvalds/subscriptions", "organizations_url": "https://api.github.com/users/torvalds/orgs", "repos_url": "https://api.github.com/users/torvalds/repos", "events_url": "https://api.github.com/users/torvalds/events{/privacy}", "received_events_url": "https://api.github.com/users/torvalds/received_events", "type": "User", "site_admin": false, "name": "Linus Torvalds", "company": "Linux Foundation", "blog": "", "location": "Portland, OR", "email": null, "hireable": null, "bio": null, "public_repos": 6, "public_gists": 0, "followers": 88851, "following": 0, "created_at": "2011-09-03T15:26:22Z", "updated_at": "2019-01-23T02:49:04Z" }
2. „Marshalling“ datových struktur do formátu JSON
Pro práci s formátem JSON v programovacím jazyku Go slouží standardní balíček nazvaný encoding/json. Všechny dnes ukázané demonstrační příklady tedy budou tento balíček importovat:
import ( ... "encoding/json" ... )
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) } ... ... ...
3. Převod celočíselných hodnot do JSONu
V dnešním prvním demonstračním příkladu, jehož zdrojový kód naleznete na adrese https://github.com/tisnik/go-fedora/blob/master/article13/01_json_marshal_basic_signed_types.go je ukázán způsob konverze celých čísel se znaménkem do formátu JSON. Připomeňme si, že v Go existuje několik celočíselných typů se znaménkem, přičemž některé typy jsou pouze jmennými aliasy:
# | Označení | Rozsah hodnot | Stručný popis |
---|---|---|---|
1 | int8 | –128 až 127 | osmibitové celé číslo se znaménkem |
2 | int16 | –32768 až 32767 | 16bitové celé číslo se znaménkem |
3 | int32 | –2147483648 až 2147483647 | 32bitové celé číslo se znaménkem |
4 | int64 | –9223372036854775808 až 9223372036854775807 | 64bitové celé číslo se znaménkem |
5 | int | různý | odpovídá buď typu int32 nebo int64 |
6 | rune | –2147483648 až 2147483647 | alias pro typ int32, používá se pro znaky |
Převod hodnot všech skupin celočíselných hodnot se znaménkem může být proveden následovně (vynechali jsme pouze typ int, který je aliasem pro int32 nebo int64):
package main import ( "encoding/json" "fmt" ) func main() { var a int8 = -10 var b int16 = -1000 var c int32 = -10000 var d int32 = -1000000 var r1 rune = 'a' var r2 rune = '\x40' var r3 rune = '\n' var r4 rune = '\u03BB' a_json, _ := json.Marshal(a) fmt.Println(string(a_json)) b_json, _ := json.Marshal(b) fmt.Println(string(b_json)) c_json, _ := json.Marshal(c) fmt.Println(string(c_json)) d_json, _ := json.Marshal(d) fmt.Println(string(d_json)) r1_json, _ := json.Marshal(r1) fmt.Println(string(r1_json)) r2_json, _ := json.Marshal(r2) fmt.Println(string(r2_json)) r3_json, _ := json.Marshal(r3) fmt.Println(string(r3_json)) r4_json, _ := json.Marshal(r4) fmt.Println(string(r4_json)) }
Po spuštění demonstračního příkladu by se mělo zobrazit osm textových řádků, z nichž každý reprezentuje validní JSON:
-10 -1000 -10000 -1000000 97 64 10 955
Druhý demonstrační příklad, který najdete na adrese https://github.com/tisnik/go-fedora/blob/master/article13/02_json_marshal_basic_unsigned_types.go, provádí tytéž operace, ovšem nad celočíselnými hodnotami bez znaménka:
# | Označení | Rozsah hodnot | Stručný popis |
---|---|---|---|
1 | uint8 | 0 až 255 | osmibitové celé číslo bez znaménka |
2 | uint16 | 0 až 65535 | 16bitové celé číslo bez znaménka |
3 | uint32 | 0 až 4294967295 | 32bitové celé číslo bez znaménka |
4 | uint64 | 0 až 18446744073709551615 | 64bitové celé číslo bez znaménka |
5 | uint | různý | odpovídá buď typu uint32 nebo uint64 |
6 | byte | 0 až 255 | alias pro typ uint8 |
Zdrojový kód tohoto demonstračního příkladu vypadá takto:
package main import ( "encoding/json" "fmt" ) func main() { var b8 byte = 0x42 var a uint8 = 10 var b uint16 = 1000 var c uint32 = 10000 var d uint32 = 1000000 b8_json, _ := json.Marshal(b8) fmt.Println(string(b8_json)) a_json, _ := json.Marshal(a) fmt.Println(string(a_json)) b_json, _ := json.Marshal(b) fmt.Println(string(b_json)) c_json, _ := json.Marshal(c) fmt.Println(string(c_json)) d_json, _ := json.Marshal(d) fmt.Println(string(d_json)) }
Po spuštění tohoto příkladu získáme pět řádků – pět korektních JSONů:
66 10 1000 10000 1000000
4. Chování systému při pokusu o převod hodnot, které nemají podporu v JSONu
Hodnoty některých datových typů programovacího jazyka Go ovšem nemají ve formátu JSON přímý ekvivalent a proto nejsou převoditelné (alespoň ne automaticky). Týká se to například i datových typů complex64 a complex128, kterými jsou v jazyku Go reprezentována komplexní čísla (dvojice hodnot typu float32 popř. float64):
var a complex64 = -1.5 + 0i var b complex64 = 1.5 + 1000i var c complex64 = 1e30 + 1e30i var d complex64 = 1i
Převod (prozatím bez jakékoli kontroly průběhu převodu) je implementován v dnešním třetím demonstračním příkladu s tímto zdrojovým kódem:
package main import ( "encoding/json" "fmt" ) func main() { var a complex64 = -1.5 + 0i var b complex64 = 1.5 + 1000i var c complex64 = 1e30 + 1e30i var d complex64 = 1i a_json, _ := json.Marshal(a) fmt.Println(string(a_json)) b_json, _ := json.Marshal(b) fmt.Println(string(b_json)) c_json, _ := json.Marshal(c) fmt.Println(string(c_json)) d_json, _ := json.Marshal(d) fmt.Println(string(d_json)) }
Po spuštění získáme pouze čtveřici prázdných řádků (tedy prázdných řetězců), což samozřejmě neodpovídá očekávaným číselným hodnotám:
Z tohoto důvodu zdrojový kód programu nepatrně upravíme takovým způsobem, aby bylo možné zkontrolovat chyby, k nimž může při převodu (tedy při marshallingu) do JSONu dojít:
package main import ( "encoding/json" "fmt" ) func main() { var a complex64 = -1.5 + 0i var b complex64 = 1.5 + 1000i var c complex64 = 1e30 + 1e30i var d complex64 = 1i a_json, a_err := json.Marshal(a) fmt.Println(string(a_json)) fmt.Println(a_err) b_json, b_err := json.Marshal(b) fmt.Println(string(b_json)) fmt.Println(b_err) c_json, c_err := json.Marshal(c) fmt.Println(string(c_json)) fmt.Println(c_err) d_json, d_err := json.Marshal(d) fmt.Println(string(d_json)) fmt.Println(d_err) }
Po spuštění takto upraveného příkladu je již jasné, že se konverze nezadařila – po každém prázdném řádku (výsledek nepovedené konverze) je totiž vypsána i chyba, ke které došlo:
json: unsupported type: complex64 json: unsupported type: complex64 json: unsupported type: complex64 json: unsupported type: complex64
5. Převod polí a řezů do JSONu
S JSONy obsahujícími jedinou skalární hodnotu se nesetkáme příliš často (i když některé webové služby vrací jednoduché řetězce obsahující stav aplikace), proto se nyní podívejme na způsob práce se strukturovanými daty. Začneme s poli (arrays), popř. s řezy, které se do JSONu provádí naprosto stejným způsobem jako pole. Vyzkoušíme si převod polí, v nichž jsou typy prvků shodné:
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"}
Převádět samozřejmě můžeme i dvourozměrné (popř. i vícerozměrné) pole:
matice := [4][3]float32{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9}, {0, -1, 0}, }
V případě, že je nutné pracovat s poli, v nichž mohou mít prvky rozdílné typy hodnot (což je v JSONu relativně častý požadavek), můžeme si v programovacím jazyce Go pomoci poli s prvky typu „prázdné rozhraní“ (přesněji se všemi typy, které prázdné rozhraní implementují – to jsou všechny typy):
a5 := []interface{}{1, "root", 3.1415, true, []int{1, 2, 3, 4}}
Následuje úplný zdrojový kód dnešního čtvrtého demonstračního příkladu, v němž k marshallingu polí dochází:
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)) }
První tři pole se převedou do formátu JSON takto:
[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]
Další pole obsahuje řetězce, které mohou vypadat následovně (existuje i druhá varianta s apostrofy namísto uvozovek):
["www","root","cz"]
Následuje pole prvků různých typů (resp. hodnot implementujících prázdné rozhraní):
[1,"root",3.1415,true,[1,2,3,4]]
A konečně posledním případem je dvourozměrné pole hodnot typu float32, které sice nejsou v JSONu přímo podporovány, ale je proveden jejich převod na float64/double (což je převod, v němž nedochází ke ztrátě přesnosti ani rozsahu). Výsledek bude následující:
[[1,2,3],[4,5,6],[7,8,9],[0,-1,0]]
6. Struktury (záznamy) a jejich přímý převod do JSONu
Do JSONu pochopitelně můžeme převádět i jednotlivé struktury. Struktura se zkonvertuje do JSONu ve formě objektu (což je označení pro hodnotu s atributy). Ovšem musíme si dát pozor na to, že převedeny budou jen ty položky záznamu, jejichž jméno začíná velkým písmenem. Ostatně se o tom můžeme snadno přesvědčit při překladu a spuštění pátého demonstračního příkladu:
package main import ( "encoding/json" "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č"} user1_json, _ := json.Marshal(user1) fmt.Println(string(user1_json)) user2_json, _ := json.Marshal(user2) fmt.Println(string(user2_json)) }
První struktura se převede na prázdný objekt (má položky pojmenované malými písmeny), druhá se již převede korektně:
{} {"Id":1,"Name":"Pepek","Surname":"Vyskoč"}
Často se taktéž setkáme s poli, jejichž prvky jsou struktury. Převod takové hierarchické datové struktury je stejně přímočarý jako v předchozím příkladu:
package main import ( "encoding/json" "fmt" ) type User struct { Id uint32 Name string Surname string } 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"}, } users_json, _ := json.Marshal(users) fmt.Println(string(users_json)) }
S výsledkem (JSON není nijak naformátovaný):
[{"Id":1,"Name":"Pepek","Surname":"Vyskoč"},{"Id":2,"Name":"Pepek","Surname":"Vyskoč"},{"Id":3,"Name":"Josef","Surname":"Vyskočil"}]
Výsledek po naformátování:
[ { "Id": 1, "Name": "Pepek", "Surname": "Vyskoč" }, { "Id": 2, "Name": "Pepek", "Surname": "Vyskoč" }, { "Id": 3, "Name": "Josef", "Surname": "Vyskočil" } ]
Opět platí, že prvky struktury, jejichž název začíná malým písmenem, nebudou převedeny, což je ukázáno v dalším příkladu:
package main import ( "encoding/json" "fmt" ) type User struct { id uint32 name string surname string } 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"}, } users_json, _ := json.Marshal(users) fmt.Println(string(users_json)) }
Výsledek – pole s prázdnými objekty:
[{},{},{}]
7. Jedno z nejčastějších použití: mapy struktur (záznamů)
Pravděpodobně nejčastěji se při práci s formátem JSON setkáme s mapami (asociativními poli), jejichž hodnotami jsou záznamy. Mapy mají v JSONu jedno omezené – klíči mohou být řetězce (toto omezení v programovacím jazyce Go neplatí).
V dalším příkladu je ukázán převod mapy s dvěma dvojicemi klíč-hodnota. Klíče jsou typu string, hodnotami jsou struktury/záznamy s předem známými prvky:
package main import ( "encoding/json" "fmt" ) type User struct { Id uint32 Name string Surname string } func main() { m1 := make(map[string]User) m1["user-id-1"] = User{ Id: 1, Name: "Pepek", Surname: "Vyskoč"} m1["user-id-2"] = User{ Id: 2, Name: "Josef", Surname: "Vyskočil"} m1_json, _ := json.Marshal(m1) fmt.Println(string(m1_json)) }
Výsledek:
{"user-id-1":{"Id":1,"Name":"Pepek","Surname":"Vyskoč"},"user-id-2":{"Id":2,"Name":"Josef","Surname":"Vyskočil"}}
Po naformátování:
{ "user-id-1": { "Id": 1, "Name": "Pepek", "Surname": "Vyskoč" }, "user-id-2": { "Id": 2, "Name": "Josef", "Surname": "Vyskočil" } }
Dejte si ovšem skutečně pozor na to, aby klíče mapy byly skutečně řetězci. Pokud se použije jiný datový typ, nebude převod proveden, i když například použít této struktury jako klíče je v Go legální:
type Key struct { Id uint32 Role string }
O chování funkce pro marshalling se můžeme snadno přesvědčit:
package main import ( "encoding/json" "fmt" ) type Key struct { Id uint32 Role string } type User struct { Id uint32 Name string Surname string } func main() { m1 := make(map[Key]User) m1[Key{1, "admin"}] = User{ Id: 1, Name: "Pepek", Surname: "Vyskoč"} m1[Key{2, "user"}] = User{ Id: 2, Name: "Josef", Surname: "Vyskočil"} m1_json, _ := json.Marshal(m1) fmt.Println(string(m1_json)) }
Výsledkem bude v tomto případě prázdný řádek:
8. Složitější (vnořené) datové struktury, změna názvů klíčů ve výsledném JSONu
Samozřejmě se můžeme pokusit i o marshalling struktur/záznamů, jejichž prvky jsou opět záznamy. V dalším příkladu se serializuje mapa obsahující struktury, přičemž v prvku Ids je uložena další struktura:
package main import ( "encoding/json" "fmt" ) type Identifiers struct { UID uint32 GID uint32 } type User struct { Name string Surname string Sign []byte Enabled bool Ids Identifiers } func main() { mapOfUsers := make(map[string]User) mapOfUsers["user-id-1"] = User{ Ids: Identifiers{1, 1}, Name: "Pepek", Surname: "Vyskoč", Enabled: true, Sign: []byte{0,0,0,0}} mapOfUsers["user-id-2"] = User{ Ids: Identifiers{2, 1}, Name: "Josef", Surname: "Vyskočil", Enabled: false, Sign: []byte{42, 10, 0, 255}} mapOfUsers["user-id-3"] = User{ Ids: Identifiers{3, 1}, Name: "Varel", Surname: "Frištenský"} mapOfUsers_json, _ := json.Marshal(mapOfUsers) fmt.Println(string(mapOfUsers_json)) }
S tímto výsledkem:
{"user-id-1":{"Name":"Pepek","Surname":"Vyskoč","Sign":"AAAAAA==","Enabled":true,"Ids":{"UID":1,"GID":1}},"user-id-2":{"Name":"Josef","Surname":"Vyskočil","Sign":"KgoA/w==","Enabled":false,"Ids":{"UID":2,"GID":1}},"user-id-3":{"Name":"Varel","Surname":"Frištenský","Sign":null,"Enabled":false,"Ids":{"UID":3,"GID":1}}}
Po naformátování:
{ "user-id-1": { "Name": "Pepek", "Surname": "Vyskoč", "Sign": "AAAAAA==", "Enabled": true, "Ids": { "UID": 1, "GID": 1 } }, "user-id-2": { "Name": "Josef", "Surname": "Vyskočil", "Sign": "KgoA/w==", "Enabled": false, "Ids": { "UID": 2, "GID": 1 } }, "user-id-3": { "Name": "Varel", "Surname": "Frištenský", "Sign": null, "Enabled": false, "Ids": { "UID": 3, "GID": 1 } } }
Velmi často se ovšem setkáme s požadavkem na to, aby měly položky v JSONu odlišné označení – ostatně pojmenování položek stylem CamelCase není ve světě JSON příliš běžné. Řešení tohoto problému existuje, i když není příliš elegantní – v Go jsou totiž podporovány takzvané „tagged structs“, což jsou běžné struktury/záznamy, za jejichž položkami jsou v řetězci zapsaném ve zpětných apostrofech uvedeny příslušné názvy, které se mají v JSONu objevit:
type Identifiers struct { UID uint32 `json:"user-id"` GID uint32 `json:"group-id"` }
Nevýhodou tohoto způsobu deklarace je fakt, že obsah řetězců není překladačem nijak kontrolován, takže se o případných problémech (chybějící uvozovky atd.) dozvíme až v čase běhu aplikace (ideální z testů).
Chování si můžeme snadno odzkoušet na mírně upraveném příkladu:
package main import ( "encoding/json" "fmt" ) type Identifiers struct { UID uint32 `json:"user-id"` GID uint32 `json:"group-id"` } type User struct { Name string `json:"user-name"` Surname string `json:"user-surname"` Sign []byte Enabled bool `json:"user-login-enabled"` Ids Identifiers } func main() { mapOfUsers := make(map[string]User) mapOfUsers["user-id-1"] = User{ Ids: Identifiers{1, 1}, Name: "Pepek", Surname: "Vyskoč", Enabled: true, Sign: []byte{0, 0, 0, 0}} mapOfUsers["user-id-2"] = User{ Ids: Identifiers{2, 1}, Name: "Josef", Surname: "Vyskočil", Enabled: false, Sign: []byte{42, 10, 0, 255}} mapOfUsers["user-id-3"] = User{ Ids: Identifiers{3, 1}, Name: "Varel", Surname: "Frištenský"} mapOfUsers_json, _ := json.Marshal(mapOfUsers) fmt.Println(string(mapOfUsers_json)) }
Nyní je výstup odlišný:
{"user-id-1":{"user-name":"Pepek","user-surname":"Vyskoč","Sign":"AAAAAA==","user-login-enabled":true,"Ids":{"user-id":1,"group-id":1}},"user-id-2":{"user-name":"Josef","user-surname":"Vyskočil","Sign":"KgoA/w==","user-login-enabled":false,"Ids":{"user-id":2,"group-id":1}},"user-id-3":{"user-name":"Varel","user-surname":"Frištenský","Sign":null,"user-login-enabled":false,"Ids":{"user-id":3,"group-id":1}}}
Opět si výsledek naformátujeme:
{ "user-id-1": { "user-name": "Pepek", "user-surname": "Vyskoč", "Sign": "AAAAAA==", "user-login-enabled": true, "Ids": { "user-id": 1, "group-id": 1 } }, "user-id-2": { "user-name": "Josef", "user-surname": "Vyskočil", "Sign": "KgoA/w==", "user-login-enabled": false, "Ids": { "user-id": 2, "group-id": 1 } }, "user-id-3": { "user-name": "Varel", "user-surname": "Frištenský", "Sign": null, "user-login-enabled": false, "Ids": { "user-id": 3, "group-id": 1 } } }
9. Export speciálních hodnot do JSONu
Ještě si musíme ukázat chování funkce Marshal při exportu některých speciálních hodnot do formátu JSON. Nejdříve si uveďme zdrojový kód celého příkladu; pod samotným kódem jsou pak uvedeny poznámky pro každou z hodnot:
package main import ( "encoding/json" "fmt" ) func main() { v1 := "" v2 := false v3 := true var v4 *int var slice1 []int slice2 := []int{} var map1 map[string]string map2 := make(map[string]string) map3 := map[string]string{} // https://speakerdeck.com/campoy/understanding-nil var iface interface{} = nil v1_json, _ := json.Marshal(v1) fmt.Println(string(v1_json)) v2_json, _ := json.Marshal(v2) fmt.Println(string(v2_json)) v3_json, _ := json.Marshal(v3) fmt.Println(string(v3_json)) v4_json, _ := json.Marshal(v4) fmt.Println(string(v4_json)) slice1_json, _ := json.Marshal(slice1) fmt.Println(string(slice1_json)) slice2_json, _ := json.Marshal(slice2) fmt.Println(string(slice2_json)) map1_json, _ := json.Marshal(map1) fmt.Println(string(map1_json)) map2_json, _ := json.Marshal(map2) fmt.Println(string(map2_json)) map3_json, _ := json.Marshal(map3) fmt.Println(string(map3_json)) iface_json, _ := json.Marshal(iface) fmt.Println(string(iface_json)) var f = func() {} f_json, _ := json.Marshal(f) fmt.Println(string(f_json)) }
Hodnota | Zápis | Výsledek v JSONu |
---|---|---|
prázdný řetězec | "" | "" |
konstanta false | false | false |
konstanta true | true | true |
ukazatel (nulový) | *int | null |
řez bez alokace paměti | []int | null |
prázdný řez (nulová kapacita) | []int{} | [] |
neinicializovaná mapa (bez alokace) | map[string]string | null |
prázdná mapa | make(map[string]string) | {} |
prázdná mapa | map[string]string{} | {} |
„nulové“ rozhraní | interface{} | null |
funkce | func() {} | × |
10. Import dat ve formátu JSON
Ve druhé části článku se zaměříme na popis importu dat z formátu JSON do interních datových struktur programovacího jazyka Go. Pro tuto operaci, která se nazývá unmarshalling, je následující hlavička:
func Unmarshal(data []byte, v interface{}) error
Vstupem je v tomto případě pole (řez) bajtů, výstup je vrácen přes ukazatel předaný ve druhém parametru (což znamená, že se musíme sami postarat o případnou alokaci paměti pro strukturu či pro mapu). Samozřejmě, že při unmarshallingu může dojít k nějaké chybě, která je vrácena volající funkci. Pokud k chybě nedošlo, je návratová hodnota rovna nil.
11. Import jednoduché struktury (záznamu) se známým obsahem
Nejjednodušší je situace ve chvíli, kdy přesně známe strukturu dat. Pokud se v JSONu přenáší informace o struktuře (záznamu, objektu), lze tuto strukturu deklarovat jako datový typ, vytvořit proměnnou tohoto typu a následně zavolat výše zmíněnou funkci Unmarshal:
type User struct { ... ... ... } var user User json.Unmarshal(bytes, &user)
Podívejme se, jak by mohl vypadat celý příklad, v němž je JSON přímo uložen ve formě řetězcového literálu:
package main import ( "encoding/json" "fmt" ) type User struct { Id uint32 Name string Surname string } func main() { input_json := `{ "Id":1, "Name":"Pepek", "Surname":"Vyskoč" }` fmt.Println("Input:") fmt.Println(input_json) bytes := []byte(input_json) var user User json.Unmarshal(bytes, &user) fmt.Println("\nOutput:") fmt.Println(user) fmt.Println("\nFields:") fmt.Printf("ID: %d\n", user.Id) fmt.Printf("Name: %s\n", user.Name) fmt.Printf("Surname: %s\n", user.Surname) }
S výsledky:
Input: { "Id":1, "Name":"Pepek", "Surname":"Vyskoč" } Output: {1 Pepek Vyskoč} Fields: ID: 1 Name: Pepek Surname: Vyskoč
Samozřejmě nám nic nebrání si příslušný JSON načíst ze souboru:
package main import ( "encoding/json" "fmt" "io/ioutil" "log" ) type User struct { Id uint32 Name string Surname string } func main() { input_json_as_bytes, err := ioutil.ReadFile("user.json") if err != nil { log.Fatal(err) } fmt.Println("Input (bytes):") fmt.Println(input_json_as_bytes) fmt.Println("\nInput (string):") fmt.Println(string(input_json_as_bytes)) var user User json.Unmarshal(input_json_as_bytes, &user) fmt.Println("\nOutput:") fmt.Println(user) fmt.Println("\nFields:") fmt.Printf("ID: %d\n", user.Id) fmt.Printf("Name: %s\n", user.Name) fmt.Printf("Surname: %s\n", user.Surname) }
Příklad výstupu:
Input (bytes): [123 10 32 32 32 32 34 73 100 34 58 49 44 10 32 32 32 32 34 78 97 109 101 34 58 34 80 101 112 101 107 34 44 10 32 32 32 32 34 83 117 114 110 97 109 101 34 58 34 86 121 115 107 111 196 141 34 10 125 10] Input (string): { "Id":1, "Name":"Pepek", "Surname":"Vyskoč" } Output: {1 Pepek Vyskoč} Fields: ID: 1 Name: Pepek Surname: Vyskoč
12. Import polí z JSONu
Při importu polí využijeme skutečnosti, že v případě použití řezů je možné pole, které je řezem používáno, automaticky zvětšovat při přidávání nových prvků. Tuto operaci za nás provede přímo knihovna pro unmarshalling:
var numbers []int json.Unmarshal(input_json_as_bytes, &numbers)
Opět si ukažme úplný zdrojový kód tohoto demonstračního příkladu:
package main import ( "encoding/json" "fmt" "io/ioutil" "log" ) func main() { input_json_as_bytes, err := ioutil.ReadFile("numbers.json") if err != nil { log.Fatal(err) } fmt.Println("Input (bytes):") fmt.Println(input_json_as_bytes) fmt.Println("\nInput (string):") fmt.Println(string(input_json_as_bytes)) var numbers []int json.Unmarshal(input_json_as_bytes, &numbers) fmt.Println("\nOutput:") fmt.Println(numbers) fmt.Println("\nItems:") for i, item := range numbers { fmt.Printf("%d\t%d\n", i, item) } }
Příklad výstupu:
Input (bytes): [91 49 44 49 48 44 50 44 57 44 51 44 56 44 52 44 55 44 53 44 54 93 10] Input (string): [1,10,2,9,3,8,4,7,5,6] Output: [1 10 2 9 3 8 4 7 5 6] Items: 0 1 1 10 2 2 3 9 4 3 5 8 6 4 7 7 8 5 9 6
Můžeme samozřejmě zpracovat i pole struktur, tj. tento vstupní soubor:
[ { "Id":1, "Name":"Pepek", "Surname":"Vyskoč" }, { "Id":2, "Name":"Pepek", "Surname":"Vyskoč" }, { "Id":3, "Name":"Josef", "Surname":"Vyskočil" } ]
A to následujícím programem:
package main import ( "encoding/json" "fmt" "io/ioutil" "log" ) type User struct { Id uint32 Name string Surname string } func main() { input_json_as_bytes, err := ioutil.ReadFile("users.json") if err != nil { log.Fatal(err) } fmt.Println("Input (bytes):") fmt.Println(input_json_as_bytes) fmt.Println("\nInput (string):") fmt.Println(string(input_json_as_bytes)) var users []User json.Unmarshal(input_json_as_bytes, &users) fmt.Println("\nOutput:") fmt.Println(users) fmt.Println("\nUsers:") for i, user := range users { fmt.Printf("%d\t%d\t%s\t%s\n", i, user.Id, user.Name, user.Surname) } }
Výstup by mohl vypadat takto:
Input (bytes): [91 10 32 32 32 32 123 10 32 32 32 32 32 32 32 32 34 73 100 34 58 49 44 10 32 32 32 32 32 32 32 32 34 78 97 109 101 34 58 34 80 101 112 101 107 34 44 10 32 32 32 32 32 32 32 32 34 83 117 114 110 97 109 101 34 58 34 86 121 115 107 111 196 141 34 10 32 32 32 32 125 44 10 32 32 32 32 123 10 32 32 32 32 32 32 32 32 34 73 100 34 58 50 44 10 32 32 32 32 32 32 32 32 34 78 97 109 101 34 58 34 80 101 112 101 107 34 44 10 32 32 32 32 32 32 32 32 34 83 117 114 110 97 109 101 34 58 34 86 121 115 107 111 196 141 34 10 32 32 32 32 125 44 10 32 32 32 32 123 10 32 32 32 32 32 32 32 32 34 73 100 34 58 51 44 10 32 32 32 32 32 32 32 32 34 78 97 109 101 34 58 34 74 111 115 101 102 34 44 10 32 32 32 32 32 32 32 32 34 83 117 114 110 97 109 101 34 58 34 86 121 115 107 111 196 141 105 108 34 10 32 32 32 32 125 10 93 10] Input (string): [ { "Id":1, "Name":"Pepek", "Surname":"Vyskoč" }, { "Id":2, "Name":"Pepek", "Surname":"Vyskoč" }, { "Id":3, "Name":"Josef", "Surname":"Vyskočil" } ] Output: [{1 Pepek Vyskoč} {2 Pepek Vyskoč} {3 Josef Vyskočil}] Users: 0 1 Pepek Vyskoč 1 2 Pepek Vyskoč 2 3 Josef Vyskočil
13. Import map obsahujících struktury (záznamy)
Ani další konverze (unmarshalling) pravděpodobně nebude nijak překvapivá – načteme JSON obsahující mapu struktur:
{ "user-id-1": { "Id":1, "Name":"Pepek", "Surname":"Vyskoč" }, "user-id-2":{ "Id":2, "Name":"Josef", "Surname":"Vyskočil" } }
Do map je samozřejmě možné přidávat další prvky, což za nás opět provede samotná knihovna JSON:
package main import ( "encoding/json" "fmt" "io/ioutil" "log" ) type User struct { Id uint32 Name string Surname string } func main() { input_json_as_bytes, err := ioutil.ReadFile("users_map.json") if err != nil { log.Fatal(err) } fmt.Println("Input (bytes):") fmt.Println(input_json_as_bytes) fmt.Println("\nInput (string):") fmt.Println(string(input_json_as_bytes)) m1 := map[string]User{} json.Unmarshal(input_json_as_bytes, &m1) fmt.Println("\nOutput:") fmt.Println(m1) fmt.Println("\nUsers:") for key, user := range m1 { fmt.Printf("%s\t%d\t%s\t%s\n", key, user.Id, user.Name, user.Surname) } }
14. Specifikace klíčů zapsaných v souborech JSON
Podobně jako při marshallingu je možné i při unmarshallingu specifikovat jména jednotlivých klíčů načítaných struktur (ovšem opět bez kontroly překladačem):
type User struct { Id uint32 `json:"user-id"` Name string `json:"user-name"` Surname string }
Načítat tak budeme moci tento JSON s rozdílnými jmény klíčů:
{ "user-id-1": { "user-id":1, "user-name":"Pepek", "surname":"Vyskoč" }, "user-id-2":{ "user-id":2, "user-name":"Josef", "surname":"Vyskočil" } }
Celý zdrojový kód vypadá takto:
package main import ( "encoding/json" "fmt" "io/ioutil" "log" ) type User struct { Id uint32 `json:"user-id"` Name string `json:"user-name"` Surname string } func main() { input_json_as_bytes, err := ioutil.ReadFile("users_map_different_keys.json") if err != nil { log.Fatal(err) } fmt.Println("Input (bytes):") fmt.Println(input_json_as_bytes) fmt.Println("\nInput (string):") fmt.Println(string(input_json_as_bytes)) m1 := map[string]User{} json.Unmarshal(input_json_as_bytes, &m1) fmt.Println("\nOutput:") fmt.Println(m1) fmt.Println("\nUsers:") for key, user := range m1 { fmt.Printf("%s\t%d\t%s\t%s\n", key, user.Id, user.Name, user.Surname) } }
15. Načtení předem neznámé struktury z JSONu
Nakonec se podívejme, jak se načítá JSON s předem neznámou strukturou. V tomto případě použijeme mapu, jejímiž klíči musí být řetězce a hodnotami libovolný typ implementující prázdné rozhraní interface{}:
m1 := map[string]interface{}{}
Problém spočívá v další interpretaci hodnot, kdy je nutné použít obdobu reflexe. Ta bude vysvětlena v navazujícím článku.
package main import ( "encoding/json" "fmt" "io/ioutil" "log" ) func main() { input_json_as_bytes, err := ioutil.ReadFile("users_map_different_keys.json") if err != nil { log.Fatal(err) } fmt.Println("Input (bytes):") fmt.Println(input_json_as_bytes) fmt.Println("\nInput (string):") fmt.Println(string(input_json_as_bytes)) m1 := map[string]interface{}{} json.Unmarshal(input_json_as_bytes, &m1) fmt.Println("\nOutput:") fmt.Println(m1) fmt.Println("\nUsers:") for key, user := range m1 { fmt.Printf("%s\t%s\n", key, user) } }
16. Práce s rastrovými obrázky
V závěrečné části dnešního článku si ukážeme, jakým způsobem je možné vytvářet rastrové obrázky a exportovat je do různých formátů. Zaměříme se přitom na dva nejčastěji používané „webové“ formáty PNG a JPEG. Pro tyto formáty existuje podpora přímo v základní knihovně programovacího jazyka Go, takže není nutné instalovat žádné další balíčky. Samotné rastrové obrázky jsou reprezentovány následujícími datovými typy, jejichž definici najdeme ve standardním balíčku image, například:
Typ | Význam |
---|---|
Alpha | pixely obsahující pouze alfa složku (průhlednost) |
Alpha16 | pixely obsahující pouze alfa složku (průhlednost) v šestnáctibitové hloubce |
CMYK | obrázek používající barvový prostor CMYK |
Gray | obrázek v odstínech šedi |
Gray16 | obrázek v odstínech šedi v šestnáctibitové hloubce |
YCbCr | obrázek používající barvový prostor YCbCr |
RGBA | obrázek používající barvový prostor RGB s průhledností |
RGBA64 | obrázek používající barvový prostor RGB s průhledností |
Paletted | obrázky využívající barvovou paletu |
Existují sice i další varianty, ovšem dnes si vystačíme s typy Gray a RGBA.
17. Export rastrového obrazu do PNG
V dalším příkladu nejdříve vytvoříme rastrový obrázek o rozlišení 256×256 pixelů, přičemž bude použit barvový prostor RGBA:
img := image.NewRGBA(image.Rect(0, 0, width, height))
Obrázek vyplníme gradientním přechodem (což nyní není příliš důležité) a následně vytvoříme nový soubor pojmenovaný „test.png“, samozřejmě s kontrolou, zda otevření proběhlo korektně:
outfile, err := os.Create("test.png") if err != nil { panic(err) } defer outfile.Close()
Nakonec jednoduše do otevřeného souboru uložíme obrázek ve formátu PNG:
png.Encode(outfile, img)
Výsledek:
![](https://i.iinfo.cz/images/97/golang-13-2.png)
Obrázek 1: Obrázek exportovaný demonstračním příkladem.
Zdrojový kód tohoto příkladu vypadá následovně:
package main import ( "image" "image/color" "image/png" "os" ) const width = 256 const height = 256 func main() { img := image.NewRGBA(image.Rect(0, 0, width, height)) for x := 0; x < width; x++ { for y := 0; y < height; y++ { var red uint8 = uint8(x) var green uint8 = uint8((x + y) >> 1) var blue uint8 = uint8(y) c := color.RGBA{red, green, blue, 255} img.SetRGBA(x, y, c) } } outfile, err := os.Create("test.png") if err != nil { panic(err) } defer outfile.Close() png.Encode(outfile, img) }
18. Export rastrového obrazu do formátu JPEG
Prakticky stejným způsobem lze provést export do formátu JPEG, pouze se musí nahradit tento řádek:
png.Encode(outfile, img)
za:
jpeg.Encode(outfile, img, nil)
Povšimněte si, že v případě konverze do JPEGu musíme funkci Decode předat ještě jeden parametr, který bude vysvětlen o několik odstavců níže:
package main import ( "image" "image/color" "image/jpeg" "os" ) const width = 256 const height = 256 func main() { img := image.NewRGBA(image.Rect(0, 0, width, height)) for x := 0; x < width; x++ { for y := 0; y < height; y++ { var red uint8 = uint8(x) var green uint8 = uint8((x - y)) var blue uint8 = uint8(y) c := color.RGBA{red, green, blue, 255} img.SetRGBA(x, y, c) } } outfile, err := os.Create("test.jpeg") if err != nil { panic(err) } defer outfile.Close() jpeg.Encode(outfile, img, nil) }
![](https://i.iinfo.cz/images/97/golang-13-1.jpg)
Obrázek 2: Výsledek předchozího příkladu.
Zajímavý je poslední parametr předávaný funkci jpeg.Encode, protože nám umožňuje specifikovat další vlastnosti použité při komprimaci rastrového obrázku do JPEGu. Ovlivnit je možné zejména kvalitu výsledku, takže se podívejme, jak se výsledný obrázek změní ve chvíli, kdy nastavíme nejnižší možnou kvalitu (a tím pádem největší komprimační poměr):
package main import ( "image" "image/color" "image/jpeg" "os" ) const width = 256 const height = 256 func main() { img := image.NewRGBA(image.Rect(0, 0, width, height)) for x := 0; x < width; x++ { for y := 0; y < height; y++ { var red uint8 = uint8(x) var green uint8 = uint8((x - y)) var blue uint8 = uint8(y) c := color.RGBA{red, green, blue, 255} img.SetRGBA(x, y, c) } } outfile, err := os.Create("test2.jpeg") if err != nil { panic(err) } defer outfile.Close() jpeg.Encode(outfile, img, &jpeg.Options{Quality: 1}) }
![](https://i.iinfo.cz/images/97/golang-13-3.jpg)
Obrázek 3: Obrázek s nejnižší kvalitou a nejvyšším komprimačním poměrem.
19. Repositář s demonstračními příklady
Zdrojové kódy všech dnes popsaný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ě jeden megabajt), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- 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 - 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