失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Go语言圣经 - 第4章 复合数据类型 - 4.5 JSON

Go语言圣经 - 第4章 复合数据类型 - 4.5 JSON

时间:2023-05-08 15:51:24

相关推荐

Go语言圣经 - 第4章 复合数据类型 - 4.5 JSON

第四章 复合数据类型

基础数据类型是Go语言世界的原子

复合数据类型包括四种:slice、map、 struct、array

数组和结构体是聚合类型,它们的值由许多元素或成员构成,数组和结构体都是固定内存大小的数据结构,,相比之下,slice和map则是动态的数据结构,它们将根据动态增长

4.5 JSON

JavaScript对象表示法(JSON)是一种用于发送和接收结构化信息的标准协议。类似的协议还有XML、ASN.1和Google的Protocol Buffer等,但JASON应用的最广泛

Go语言中对JSON的支持是encoding/json包

JSON是对JavaScript中各种类型的值-字符串、数字、布尔值和对象—Unicode本文编码,它可以用有效可读的方式表示基础数据类型和聚合数据类型

基本的JSON类型有数字(十进制或科学记数法)、布尔值、字符串,其中字符串是以双引号包含的Unicode字符序列,支持和Go语言类似的反斜杠转义特性,不过JSON使用的是\Uhhhh转义数字来表示一个UTF-16编码,而不是Go语言的rune类型

这些基础类型可以通过JSON的数组和对象类型进行递归组合。

一个JSON数组是一个有序的值序列,写在一个方括号中并以逗号分隔;一个JSON数组可以用于编码Go语言的数组和slice

一个JSON对象是一个字符串到值的映射,写成以系列的name:value对形式,用花括号包含并以逗号分隔;JSON的对象类型可以用于编码Go语言的map类型(key类型是字符串)和结构体,例如:

booleantruenumber-273.15string"she said \"Hello, BF\""array ["gold","silver","bronze"]objetct{"year":1980,"event":"archery","medals":["gold","sliver","bronze"]}

思考一个程序:该程序负责收集各种电影评论并提供反馈功能,它的Movie数据类型和一个典型的表示电影的值列表如下所示:

type Movie struct {Title stringYear int `json:"released"`Color bool `json:"color.omitempty"`Actors []string}var movies = []Movie{{Title: "Casablanca",Year: 1942,Color: false,Actors: []string{"Humphrey Bogart","Ingrid Bergman"}},{Title: "Cool and Luck",Year: 1967,Color: true,Actors: []string{"Paul Newman"}},{Title: "Bullitt",Year: 1968,Color: true,Actors: []string{"Steve McQueen","Jacqueline Bisset"}},}

类似这样的数据结构特别适合JSON格式,并且在两种之间相互转换也很容易

将一个Go语言中类似movies的结构体slice转为JSON的过程叫编组,编组通过调用json.Marshal 函数完成:

data,err := json.Marshal(movies)if err != nil {log.Fatalf("JSON marchaling failed: %s",err)}fmt.Printf

Marshling函数返还一个编码后的字节slice,包含很长的字符串,并且没有空白缩进;我们将它折行以便于显示

[{"Title":"Casablanca","released":1942,"color.omitempty":false,"Actors":["Humphrey Bogart","Ingrid Bergman"]},{"Title":"Cool and Luck","released":1967,"color.omitempty":true,"Actors":["Paul Newman"]},{"Title":"Bullitt","released":1968,"color.omitempty":true,"Actors":["Steve McQueen","Jacqueline Bisset"]}]

这种紧凑的格式虽然包含了全部信息,但是很难阅读,另一个函数则能产生整齐的缩进,该函数有两个额外的参数用于表示每一行输出的前缀和每一个层级的缩进

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

[{"Title": "Casablanca","released": 1942,"color.omitempty": false,"Actors": ["Humphrey Bogart","Ingrid Bergman"]},{"Title": "Cool and Luck","released": 1967,"color.omitempty": true,"Actors": ["Paul Newman"]},{"Title": "Bullitt","released": 1968,"color.omitempty": true,"Actors": ["Steve McQueen","Jacqueline Bisset"]}]

在编码时,默认使用Go语言结构体成员的名字作为JSON对象(通过reflect反射技术),只有导出的结构体成员才会被编码

结构体tag:一个结构体成员Tag是在编译阶段关联到该成员的元信息字符串。结构体成员的Tag可以是任意字符串面值,但是通常是以系列空格分隔的key:“value”键值对序列,因为值中含有双引号字符,因此Tag一般用原生字符串面值的形式书写

Year int `json:"released"`Color bool `json:"color.omitempty"`

json开头的键名对应的值用于控制encoding/json包的编码和解码行为,并且encoding/…下面其他的包也遵循这个约定。成员Tag中json对应值的第一部分用于指定JSON对象的名字,比如将Go语言中的TotalCount 成员对应到JSON中的total_count对象。Color成员的Tag还带了一个额外的omitempty选项,表示当Go语言结构体为空或零值时不生成JSON对象。果然,Casablanca是一个黑白电影,并没有输出Color成员

编码的逆操作时解码,对应将JSON数据解码为Go语言的数据结构,Go语言中一般叫unmarshaling,通过json.Unmashal函数完成

如下将JSON格式的电影数据解码为一个结构体slice,结构体中只有Title成员。通过定义合适的Go语言数据结构,我们可以选择性的解码

JSON中感兴趣的成员,这样其他JSON成员会被忽略

var titles []struct{Title string}if err2 := json.Unmarshal(data,&titles);err2 != nil{log.Fatalf("JSON unshaling failed: %s ",err2)}fmt.Println(titles)//[{Casablanca} {Cool and Luck} {Bullitt}]

许多Web服务都提供JSON接口,通过HTTP接口发送JSON格式请求并返回JSON格式的信息。我们通过GitHub的issue查询服务来演示此用法

我们先来定义合适的类型和常量

const IssueURL = "/search/issues"type IssuesSearchResult struct {TotalCount int `json:"total_count"`Items []*Issue}type Issue struct {Number intHTMLURL string `json:"html_url"`Title stringState stringUser *UserCreateAt time.Time `json:"create_at"`Body string}type User struct {Login stringHTMLURL string `json:"html_url"`}

和前面一样,即使对应的JSON对象名是小写字母,每个结构体的成员名也是声明为大写字母开头的。因为有些JSON成员名字和Go结构体成员名字并不相同。同样,在解码的时候也需要做同样的处理,Github服务返回的信息比我们定义的要多很多

SearchIssues函数发出一个HTTP请求,然后解码返回的JSON格式的结果,因为用户提供的查询条件可能包含类似?和&之类的特殊字符,为了避免对URL造成冲突,我们用url.QueryEscape来对查询中的特殊字符进行转义操作

func SearchIssues(terms []string) (*IssuesSearchResult, error) {q := url.QueryEscape(strings.Join(terms, " "))resp, err := http.Get(IssueURL + "?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 IssuesSearchResultif err := json.NewDecoder(resp.Body).Decode(&result); err != nil {resp.Body.Close()return nil, err}resp.Body.Close()return &result, nil}

在之前,我们使用了json.Unmarshal函数来将JSON格式的字符串解码为字节slice,但是在这个例子中,我们使用了基于流式解码器json.Encoder编码对象

我们调用Decode方法来填充变量,这里有多种方法可以格式化结构。下面是最简单的一种,以一个固定宽度打印每一个issue,但是在下一节我们将看到如何利用模板来输出复杂的格式

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

通过命令行参数指定检索条件,即可得到结果,Github的Web服务接口Https:///v3/包含了更多特性

如果觉得《Go语言圣经 - 第4章 复合数据类型 - 4.5 JSON》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。