JSON

JavaScript对象简谱(JSON, Java Script Object Notation)是一种用于发送和接收结构化信息的标准协议。在类似的协议中,JSON并不是唯一的一个标准协议。 XML(§7.14)、ASN.1和Google的Protocol Buffers都是类似的协议,并且有各自的特色,但是由于简洁性、可读性和流行程度等原因,JSON是应用最广泛的一个。

Go语言对于这些标准格式的编码和解码都有良好的支持,由标准库中的encoding/jsonencoding/xmlencoding/asn1等包提供支持(译注:Protocol Buffers的支持由github.com/golang/protobuf包提供),并且这类包都有着相似的API接口。本节,我们将对重要的encoding/json包的用法做个概述。

JSON是对JavaScript中各种类型的值(字符串、数字、布尔值和对象)的unicode文本编码。它可以用有效可读的方式表示第三章的基础数据类型和本章的数组、slice、结构体和map等聚合数据类型。

基本的JSON类型有数字(十进制或科学记数法)布尔值(true或false)、字符串,其中字符串是以双引号包含的Unicode字符序列,支持和Go语言类似的反斜杠转义特性,不过JSON使用的是\Uhhhh转义数字来表示一个UTF-16编码(译注:UTF-16和UTF-8一样是一种变长的编码,有些Unicode码点较大的字符需要用4个字节表示;而且UTF-16还有大端和小端的问题),而不是Go语言的rune类型。

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

  • 一个JSON数组是一个有序的值序列,写在一个方括号中并以逗号分隔
  • 一个JSON数组可以用于编码Go语言的数组和slice
  • 一个JSON对象是一个字符串到值的映射,写成一系列的name:value对形式,用花括号包含并以逗号分隔
  • JSON的对象类型可以用于编码Go语言的map类型(key类型是字符串)和结构体。
boolean     true
number      -273.15
string      "She said \"Hello, BF\""
array       ["gold", "silver", "bronze"]
object      {"year": 1980,"event": "archery","medals": ["gold", "silver", "bronze"]}

思考这样一个应用程序,该程序负责收集各种电影评论并提供反馈功能。它的Movie数据类型和一个典型的表示电影的值列表如下所示。(在结构体声明中,YearColor成员后面的字符串面值是结构体成员Tag;我们稍后会解释它的作用。)

type Movie struct {Title    stringYear     int `json:"release"`         // 第三个部分是tagColor    bool `json:color,omitempty`  // 第三个部分是tagActors   []string
}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"}}
}

这样的数据结构特别适合JSON格式,并且在两者之间相互转换也很容易。将一个Go语言中类似movies的结构体slice转为JSON的过程叫编组(marshaling)。编组通过调用json.Marshal函数完成:

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

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

[{"Title":"Casablanca","released":1942,"Actors":["Humphrey Bogart","Ingr
id Bergman"]},{"Title":"Cool Hand Luke","released":1967,"color":true,"Ac
tors":["Paul Newman"]},{"Title":"Bullitt","released":1968,"color":true,"
Actors":["Steve McQueen","Jacqueline Bisset"]}]

这种紧凑的表示形式虽然包含了全部的信息,但是很难阅读。为了生成便于阅读的格式,另一个json.MarshalIndent函数将产生整齐缩进的输出。该函数有两个额外的字符串参数用于表示每一行输出的前缀和每一个层级的缩进:

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

上面的代码将产生这样的输出(译注:在最后一个成员或元素后面并没有逗号分隔符):

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

在编码时,默认使用Go语言结构体的成员名字作为JSON的对象(通过reflect反射技术,我们将在12.6节讨论)。只有导出的结构体成员才会被编码,这也就是我们为什么选择用大写字母开头的成员名称。

细心的读者可能已经注意到,其中Year名字的成员在编码后变成了released,还有Color成员编码后变成了小写字母开头的color。这是因为结构体成员Tag所导致的。一个结构体成员Tag是和在编译阶段关联到该成员的元信息字符串:

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

结构体的成员Tag可以是任意的字符串面值,但是通常是一系列用空格分隔的key:"value"键值对序列;因为值中含有双引号字符,因此成员Tag一般用原生字符串面值的形式书写。json开头键名对应的值用于控制encoding/json包的编码和解码的行为,并且encoding/…下面其它的包也遵循这个约定。成员Tag中json对应值的第一部分用于指定JSON对象的名字,比如将Go语言中的TotalCount成员对应到JSON中的total_count对象。Color成员的Tag还带了一个额外的omitempty选项,表示当Go语言结构体成员为空或零值时不生成该JSON对象(这里false为零值)。果然,Casablanca是一个黑白电影,并没有输出Color成员。

编码的逆操作是解码,对应将JSON数据解码为Go语言的数据结构,Go语言中一般叫unmarshaling,通过json.Unmarshal函数完成。下面的代码将JSON格式的电影数据解码为一个结构体slice,结构体中只有Title成员。通过定义合适的Go语言数据结构,我们可以选择性地解码JSON中感兴趣的成员。当Unmarshal函数调用返回,slice将被只含有Title信息的值填充,其它JSON成员将被忽略。

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

许多web服务都提供JSON接口,通过HTTP接口发送JSON格式请求并返回JSON格式的信息。为了说明这一点,我们通过Github的issue查询服务来演示类似的用法。首先,我们要定义合适的类型和常量:

// Package github provides a Go API for the GitHub issue tracker.
// See https://developer.github.com/v3/search/#search-issues.
package githubimport "time"const IssuesURL = “https://api.github.com/search/issues”type IssuesSearchResult struct {TotalCount int `json:"total_count"`Item       []*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对象名是小写字母,每个结构体的成员名也是声明为大写字母开头的。因为有些JSON成员名字和Go结构体成员名字并不相同因此需要Go语言结构体成员Tag来指定对应的JSON名字。同样,在解码的时候也需要做同样的处理,GitHub服务返回的信息比我们定义的要多很多。

package githubimport ("encoding/json""fmt""net/http""net/url""strings"
)// SearchIssues queries the Github issue tracker
func SearchIssues(terms []string) (*IssuesSeachResult, error) {q = url.QueryEscape(strings.Joint(terms, " "))resp, err := http.Get(IssuesURL + "?q=" + q)if err != nil {return nil, err}// we must close resp.Body on all execution path// Chapter 5 presents defer, which makes this simplerif resp.StatusCode != http.StatusOK {resp.Body.Close()return nil, fmt.Errorf("search query failed: %s", resp.Status)}var result IssuesSearchResultif err := json.NewCoder(resp.Body).Decode(&result); err != nil {resp.Body.Close()return nil, err}resp.Body.Close()return &result, nil
}

在早些的例子中,我们使用了json.Unmarshal函数来将JSON格式的字符串解码为byte类型的slice。在早些的例子中,我们使用了json.Unmarshal函数来将JSON格式的字符串解码为字节slice。但是这个例子中,我们使用了基于流式的解码器json.Decoder,它可以对一个输入流解码JSON数据,尽管这不是必须的。除此之外,还有一个针对输出流的json.Encoder编码对象。

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

// Issues prints a table of Github issues matching the search terms
package mainimport ("fmt""log""os""gopl.io/ch4.github"
)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)}
}
$ go build gopl.io/ch4/issues
$ ./issues repo:golang/go is:open json decoder
13 issues:
#5680    eaigner encoding/json: set key converter on en/decoder
#6050  gopherbot encoding/json: provide tokenizer
#8658  gopherbot encoding/json: use bufio
#8462  kortschak encoding/json: UnmarshalText confuses json.Unmarshal
#5901        rsc encoding/json: allow override type marshaling
#9812  klauspost encoding/json: string tag not symmetric
#7872  extempora encoding/json: Encoder internally buffers full output
#9650    cespare encoding/json: Decoding gives errPhase when unmarshalin
#6716  gopherbot encoding/json: include field name in unmarshal error me
#6901  lukescott encoding/json, encoding/xml: option to treat unknown fi
#6384    joeshaw encoding/json: encode precise floating point integers u
#6647    btracey x/tools/cmd/godoc: display type kind of each named type
#4237  gjemiller encoding/base64: URLEncoding padding is optional

GitHub的Web服务接口 https://developer.github.com/v3/ 包含了更多的特性。

Go语言中的JSON处理 【Go语言圣经笔记】相关推荐

  1. c语言中return语句例子,C语言 return 语句-嗨客网

    C语言return语句教程 在 如果 return 语句使用在普通的 C语言return语句详解 语法 void fun(){ //do something return; } 说明 使用 retur ...

  2. c语言中1B是多少,C语言1、C语言中,运算对象必须是整型的运算符是【】A./B-查字典问答网...

    C语言1.C语言中,运算对象必须是整型的运算符是[]A./B.%C.++D. 2.下列标识符中,不合法的是[] A.x2B.sizeofC._123D.char2 5.设d是double型变量,若要从 ...

  3. 简述c语言中break的作用,c语言break什么意思?

    break在一些计算机语言中是保留字,其作用大多情况下是终止上一层的循环,以C语言来说,break在switch(开关语句)中在执行一条case后跳出语句的作用. 1. C语言中的break : br ...

  4. ab 是什么意思C语言中,a/b在c语言中什么意思,c语言中ab是什么意思

    Q1:c语言中y=(x=a+b,b+c)代表什么意思 x=a+b表达式使得x=6,其返回值为X(x,b+c)是逗号表达式,逗号表达式的值为最后一个子表达式的值所以 y=b+c y=10 Q2:C语言中 ...

  5. c语言中time函数作用,C语言中时间的基本用法小结

    前言 在我学的这些编程语言中,总是记不住它们的时间处理方式,每次用到时都要重新看,所以想着在这里记录下来,也方便用到时查找,也方便有需要的朋友们参考. time_t和struct tm 在C语言中用t ...

  6. c语言中stible什么作用,C语言程序设计课后练习辅导

    <C语言程序设计课后练习辅导>由会员分享,可在线阅读,更多相关<C语言程序设计课后练习辅导(54页珍藏版)>请在人人文库网上搜索. 1.C语言程序设计课后练习辅导资料题型题干选 ...

  7. c语言中getenv的作用,C语言putenv()函数和getenv()函数的使用详解

    C语言putenv()函数和getenv()函数的使用详解 C语言putenv()函数:改变或增加环境变量头文件: #include4 定义函数: int putenv(const char * st ...

  8. Go 反射机制详解及实例 【Go语言圣经笔记】

    反射 Go语言提供了一种机制,能够在运行时更新变量或检查它们的值.调用它们的方法和它们支持的内在操作,而不需要在编译时就知道这些变量的具体类型.这种机制被称为反射(这里反射的定义和其他语言大体相同). ...

  9. Go 接口及其相关机制深入解读加源码剖析 【Go语言圣经笔记】

    接口 接口类型是对其它类型行为的抽象和概括:因为接口类型不会和特定的实现细节绑定在一起,通过这种抽象的方式我们可以让我们的函数更加灵活和更具有适应能力. 很多面向对象的语言都有相似的接口概念,但Go语 ...

最新文章

  1. hdu 1520 没有上司的晚会
  2. STL中heap算法(堆算法)
  3. [Web 前端] mobx教程(二)-mobx主要概念
  4. 201521123113 《Java程序设计》第5周学习总结
  5. 易于使用的单位和集成代码
  6. 基于JAX-WS的webService开发实例
  7. CentOS7种搭建FTP服务器 1
  8. unity网站服务器,Unity基础网络服务器通信
  9. immutability因React官方出镜之使用总结分享!
  10. 禁止绿盟扫描oracle,Oracle Enterprise Manager Grid Control JSP代码执行漏洞(CVE-2010-3600)
  11. java工程师英文简历_java软件工程师英文简历模板
  12. 30天自制操作系统(day10)
  13. 什么是网关?网关的作用?
  14. 【python】backgroundremover 去除背景
  15. 基于QT封装的音视频播放时间轴控件
  16. 华联股份:范本式改造,BHG Mall全新升级开辟商业新契机
  17. Door to Door 美国电影 《永不放弃》
  18. 论文编辑与投稿——引用文献(References)中作者名字中“姓”、“名”应该如何组织
  19. J - [永不止步-2017]_区间第K大-线段树维护
  20. icordova拍照_ionic2/3实战-添加拍照功能cordova-plugin-camera

热门文章

  1. LIS3DH双击测试
  2. xhr如何发送post请求_ajax-如何发送ajax请求
  3. 21- JavaScript 事件简介
  4. 新手干货 Apache Ofbiz介绍+安装到使用
  5. JavaScript实现监控上传和下载进度
  6. React-Router V6 传参,类式组件用ref搭个桥接收。
  7. 59-Java:String类案例:验证码、登录、隐私号码
  8. 构建 Snowpack + React + Typescript + Electron的Desktop App
  9. mysql varchar2多少合适_MySQL中varchar最大长度是多少(仅学习)
  10. 联想小新Pro13解决黑苹果下麦克风不可用(曲线救国)