#《Go 语音圣经(中文版)》

编组(marshaling)函数会返回一个编码后的字节slice,包含很长的字符串,并且没有空白缩进。

json.MarshalIndent函数产生整齐缩进的输出,该函数有两个额外的字符串参数用于表示每一行输出的前缀和每一个层级的缩进。[1]

一个结构体成员Tag是和在编译阶段关联到该成员的元信息字符串。

omitempty选项表示当Go语言结构体成员为空或零值时不生成该JSON对象(这里false为零值)。

#文本和HTML模板

生成模板的输出需要两个处理步骤。第一步是要分析模板并转为内部表示,然后基于指定的输入执行模板。
分析模板部分一般只需要执行一次。

  1. template.New先创建并返回一个模板。
  2. Funcs方法将daysAgo等自定义函数注册到模板中,并返回模板。
  3. 调用Parse函数分析模板。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main

/*
@Time : 2021/5/20 19:43
@Author : onns
@File : 4.10.go
*/

import (
"encoding/json"
"fmt"
"log"
)

type Movie struct {
Title string
Year int `json:"relased"`
Color bool `json:"color,omitempty"`
Actors []string
}

func main() {
var movies = []Movie{
{Title: "Casablanca", Year: 1942, Color: false,
Actors: []string{"Humphrey Bogart", "Ingrid Bergman"}},
{Title: "Cool Hand Luke", Year: 1967, Color: true,
Actors: []string{"Paul Newman"}},
{Title: "Bullitt", Year: 1968, Color: true,
Actors: []string{"Steve McQueen", "Jacqueline Bisset"}},
// ...
}
data, err := json.Marshal(movies)
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)

data2, err := json.MarshalIndent(movies, "", " ")
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data2)

var titles []struct {
Title string
}
if err = json.Unmarshal(data2, &titles); err != nil {
log.Fatalf("JSON unmarshaling failed: %s", err)
}
fmt.Println(titles)
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
[19:40:54] onns@Onns ~/Onns/code/go/go-bible/ch4 $ go run 4.10.go 
[{"Title":"Casablanca","relased":1942,"Actors":["Humphrey Bogart","Ingrid Bergman"]},{"Title":"Cool Hand Luke","relased":1967,"color":true,"Actors":["Paul Newman"]},{"Title":"Bullitt","relased":1968,"color":true,"Actors":["Steve McQueen","Jacqueline Bisset"]}]
[
{
"Title": "Casablanca",
"relased": 1942,
"Actors": [
"Humphrey Bogart",
"Ingrid Bergman"
]
},
{
"Title": "Cool Hand Luke",
"relased": 1967,
"color": true,
"Actors": [
"Paul Newman"
]
},
{
"Title": "Bullitt",
"relased": 1968,
"color": true,
"Actors": [
"Steve McQueen",
"Jacqueline Bisset"
]
}
]
[{Casablanca} {Cool Hand Luke} {Bullitt}]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package github

/*
@Time : 2021/5/20 19:46
@Author : onns
@File : ch4/github/github.go
*/

import (
"time"
)

const IssuesURL = "https://api.github.com/search/issues"

type IssuesSearchResult struct {
TotalCount int `json:"total_count"`
Items []*Issue
}

type Issue struct {
Number int
HTMLURL string `json:"html_url"`
Title string
State string
User *User
CreateAt time.Time `json:"create_at"`
Body string
}

type User struct {
Login string
HTMLURL string `json:"html_url"`
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package github

/*
@Time : 2021/5/20 19:46
@Author : onns
@File : ch4/github/search.go
*/

import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
)

func SearchIssues(terms []string) (*IssuesSearchResult, error) {
q := url.QueryEscape(strings.Join(terms, " "))
resp, err := http.Get(IssuesURL + "?q=" + q)
if err != nil {
return nil, err
}

if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, fmt.Errorf("search query failed: %s", resp.Status)
}

var result IssuesSearchResult
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
resp.Body.Close()
return nil, err
}

resp.Body.Close()
return &result, nil
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package main

/*
@Time : 2021/5/20 19:49
@Author : onns
@File : ch4/4.11.go
*/

import (
"./github"
"fmt"
"log"
"os"
)

func main() {
result, err := github.SearchIssues(os.Args[1:])
if err != nil {
log.Fatal(err)
}
fmt.Printf("%d issues:\n", result.TotalCount)
for _, item := range result.Items {
fmt.Printf("#%-5d %9.9s %.55s\n",
item.Number, item.User.Login, item.Title)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[19:48:17] onns@Onns ~/Onns/code/go/go-bible/ch4 $ go build 4.11.go 
[19:50:55] onns@Onns ~/Onns/code/go/go-bible/ch4 $ ./4.11 repo:golang/go is:open json decoder
60 issues:
#33416 bserdar encoding/json: This CL adds Decoder.InternKeys
#45628 pgundlach encoding/xml: add Decoder.InputPos
#43716 ggaaooppe encoding/json: increment byte counter when using decode
#42571 dsnet encoding/json: clarify Decoder.InputOffset semantics
#32779 rsc encoding/json: memoize strings during decode
#11046 kurin encoding/json: Decoder internally buffers full input
#5901 rsc encoding/json: allow per-Encoder/per-Decoder registrati
#34543 maxatome encoding/json: Unmarshal & json.(*Decoder).Token report
#36225 dsnet encoding/json: the Decoder.Decode API lends itself to m
#14750 cyberphon encoding/json: parser ignores the case of member names
#43401 opennota proposal: encoding/csv: add Reader.InputOffset method
#29035 jaswdr proposal: encoding/json: add error var to compare the
#31701 lr1980 encoding/json: second decode after error impossible
#40128 rogpeppe proposal: encoding/json: garbage-free reading of tokens
#40127 rogpeppe encoding/json: add Encoder.EncodeToken method
#45512 colin-sit encoding/json: cannot unmarshal custom interface value
#40982 Segflow encoding/json: use different error type for unknown fie
#40983 Segflow encoding/json: return a different error type for unknow
#28923 mvdan encoding/json: speed up the decoding scanner
#43513 Alexander encoding/json: add line number to SyntaxError
#41144 alvaroale encoding/json: Unmarshaler breaks DisallowUnknownFields
#16212 josharian encoding/json: do all reflect work before decoding
#6647 btracey x/tools/cmd/godoc: display type kind of each named type
#29750 jacoelho cmd/vet: stdmethods check gets confused if run on a pac
#34564 mdempsky go/internal/gcimporter: single source of truth for deco
#33835 Qhesz encoding/json: unmarshalling null into non-nullable gol
#33854 Qhesz encoding/json: unmarshal option to treat omitted fields
#30301 zelch encoding/xml: option to treat unknown fields as an erro
#26946 deuill encoding/json: clarify what happens when unmarshaling i
#22752 buyology proposal: encoding/json: add access to the underlying d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main

/*
@Time : 2021/5/20 19:53
@Author : onns
@File : ch4/4.12.go
*/

import (
"./github"
// "fmt"
"html/template"
"log"
"os"
"time"
)

const templ = `{{.TotalCount}} issues:
{{range .Items}}----------------------------------------
Number: {{.Number}}
User: {{.User.Login}}
Title: {{.Title | printf "%.64s"}}
Age: {{.CreatedAt | daysAgo}} days
{{end}}`

func daysAgo(t time.Time) int {
return int(time.Since(t).Hours() / 24)
}

func main() {
report, err := template.New("report").
Funcs(template.FuncMap{"daysAgo": daysAgo}).
Parse(templ)
if err != nil {
log.Fatal(err)
}
result, err := github.SearchIssues(os.Args[1:])
if err != nil {
log.Fatal(err)
}
if err := report.Execute(os.Stdout, result); err != nil {
log.Fatal(err)
}
}
1
2
3
4
5
6
7
8
[19:51:47] onns@Onns ~/Onns/code/go/go-bible/ch4 $ go build 4.12.go                          
[19:53:52] onns@Onns ~/Onns/code/go/go-bible/ch4 $ ./4.12 repo:golang/go is:open json decoder
60 issues:
----------------------------------------
Number: 33416
User: bserdar
Title: encoding/json: This CL adds Decoder.InternKeys
Age: 2021/05/20 19:54:35 template: report:6:10: executing "report" at <.CreatedAt>: can't evaluate field CreatedAt in type *github.Issue

  1. 在最后一个成员或元素后面并没有逗号分隔符。 ↩︎