Go 与 JSON
Go 中通过标准库encoding/json、encoding.xml、encoding/asn1和其他库对 JSON、XML、ASN.1 和其他类型的标准的编码和解码提供了良好的支持,这里对使用最多的encoding/json进行一个简要地描述。
看下面一个结构体类型(Year 和 Color 后面的字符串是「成员标签」):
type Movie struct {
Title string
Yeat int `json:"released"`
Color bool `json:"Color,omitempty"`
Actors []string
}
var movies = []Movie {
{
Title: "Casablanca",
Year: 1942,
Color: false,
Actors: []string{"A", "B"},
},
{
Title: "Cool Hand Luke",
Year: 1967,
Color: true,
Actors: []string{"C", "D"},
},
}
显然结构体很适合通过 JSON 来描述内部的数据,无论是从 Go 对象转换成 JSON 还是从 JSON 转换成 Go 对象都是容易的。
Go 的数据结构转换为 JSON:marshal
将 Go 的数据结构转换成 JSON 称为「marshal」,marshal 是通过json.Marshal来实现的:
data, err := json.Marshal(movies)
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)
Marshal 会生成一个字节 slice,其中包含一个不带有任何多余空白字符的很长的字符串。上面的结果会是这样的:
[{"Title":"Casablanca","released":1942,"Actors":["A","B"]},{"Title":"CoolHandLuke","released":1967,"color":true,"Actors":["C","D"]}]
显然上面将所有的信息都放在一行之中表示,难以阅读。为了方便阅读,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": [
"A",
"B"
]
},
{
"Title": "Cool Hand Luke",
"released": 1967,
"color": true,
"Actors": [
"C",
"D"
]
}
]
可以看到 Marshal 能够使用 Go 结构体成员的名称作为 JSON 对象中字段的名称(通过反射实现),因此,只有可以导出的成员能够转换成 JSON 对象,这也是为什么一般都将 Go 结构体中的所有成员定义为首字母大写。
仔细观察上面的 JSON 对象,我们可以发现,结构体成员的Year对应地转化为了released,Color转换为了color,这就是通过前面的成员标签field tag定义实现的。
「成员标签」定义结构体成员在编译期间关联的一些元信息:
Year int `json:"released"`
Color bool `json:"color,omitempty"`
成员标签的定义可以是任何字符串,但按照习惯,是由一串由空格分开的标签键值对key:"value"组成;因为标签的值使用双引号括起来,因此一般标签都是原生的字符串字面量。
上例中,键json控制了包encoding/json的行为,(其他encoding/...包也遵循这个规则),标签的第一部分制定了 Go 结构体成员对应 JSON 中字段的名字。成员的标签一般便是如此使用。
而 Color 标签中还有一个额外的选项:omitempty;这个选项表示:如果这个成员的值是零值或者为空,则不输出这个成员到 JSON 中。所以对于电影「Casablanse」,并没有输出成员 Color 到 JSON 中。
JSON 转换为 Go 的数据结构:unmarshal
marshal 的逆操作便是将 JSON 转换为 Go 数据结构,这个过程称为「unmarshal」,由json.Unmarshal实现,下面代码将电影的 JSON 数据转换为结构体 slice。
这里我们指定结构体中仅含 Title 这个成员,通过合理地定义 Go 的数据结构,我们可以选择解码 JSON 中哪些数据,其余数据则不会转换到结构体中:
var titles []struct{ Title string }
if err := json.Unmarshal(data, &titles); err != nil {
log.Fatalf("JSON unmarshaling failed: %s", err)
}
fmt.Println(titles) // 输出: [{Casablanca} {Cool Hand Luke}]
下面我们通过看一个例子,这个例子中我们查询一个 github 页面的 issues 接口:
1.新建一个
github包,在其中定义需要的类型和常量
这里我们固定查询的 github 仓库,并定义查询结果的结构体IssuesSearchResult。
// 包 github 提供 GitHub issue 跟踪接口的 Go API
/* 这里定义了查询 issuse 所用的类型和常量 */
package github
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
CreatedAt time.Time `json:"created_at"`
Body string // markdown 格式
}
type User struct {
Login string
HTMLURL string `json:"html_url"`
}
2.编写函数
SearchIssues发送 HTTP 请求,并将相应解析为 JSON
由于用户查询请求可能存在一些特殊字符,例如&或?这些属于 URL 的特殊字符,因此我们还要使用url.QueryEscape来保证它们拥有正确含义:
package github
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"strings"
)
func SearchIssues(terms []string) (*IssuesSearchResult, error) {
// 发起 HTTP 请求
q := url.QueryEscape(strings.Join(terms, " "))
resp, err := http.Get(IssuesURL + "?q=" + q)
if err != nil {
return nil, err
}
// 在所有的分支上面关闭 resp.Body
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("search query failed: %s", resp.Status)
}
// 将 response 解析为 JSON
var result IssuesSearchResult
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}
return &result, nil
}
不同于之前使用的json.Unmarshal,这里我们使用来流式解码器json.Decoder来对 JSON 进行解码,它能够从字节流中解码出多个 JSON 实例,相对应的,也存在用于编码的json.Encoder。
3.调用之前编写的 github 包,输出查询的 issues
// 将复合搜索条件的 issues 输出为一个表格
package main
import (
"fmt"
"log"
"os"
// 导入刚刚编写的包,包路径依照环境变量设置而不同
"github.com/Bylight/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)
}
}
最后我们运行最后编写的 main 函数,我们指定它搜索 Go 项目中 issue 跟踪接口,查找关于 JSON 编码的 Open 状态的 bug 列表:
➜ go run issues.go repo:golang/go is:open json decoder
43 issues:
33416 bserdar encoding/json: This CL adds Decoder.InternKeys
34647 babolivie encoding/json: fix byte counter increments when using d
5901 rsc encoding/json: allow override type marshaling
29035 jaswdr proposal: encoding/json: add error var to compare the
34543 maxatome encoding/json: Unmarshal & json.(*Decoder).Token report
32779 rsc proposal: encoding/json: memoize strings during decode?
28923 mvdan encoding/json: speed up the decoding scanner
11046 kurin encoding/json: Decoder internally buffers full input
...
Go 与 JSON的更多相关文章
- 使用TSQL查询和更新 JSON 数据
JSON是一个非常流行的,用于数据交换的文本数据(textual data)格式,主要用于Web和移动应用程序中.JSON 使用“键/值对”(Key:Value pair)存储数据,能够表示嵌套键值对 ...
- 【疯狂造轮子-iOS】JSON转Model系列之二
[疯狂造轮子-iOS]JSON转Model系列之二 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 上一篇<[疯狂造轮子-iOS]JSON转Model系列之一> ...
- 【疯狂造轮子-iOS】JSON转Model系列之一
[疯狂造轮子-iOS]JSON转Model系列之一 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 之前一直看别人的源码,虽然对自己提升比较大,但毕竟不是自己写的,很容易遗 ...
- Taurus.MVC 2.2 开源发布:WebAPI 功能增强(请求跨域及Json转换)
背景: 1:有用户反馈了关于跨域请求的问题. 2:有用户反馈了参数获取的问题. 3:JsonHelper的增强. 在综合上面的条件下,有了2.2版本的更新,也因此写了此文. 开源地址: https:/ ...
- .NET Core系列 : 2 、project.json 这葫芦里卖的什么药
.NET Core系列 : 1..NET Core 环境搭建和命令行CLI入门 介绍了.NET Core环境,本文介绍.NET Core中最重要的一个配置文件project.json的相关内容.我们可 ...
- 一个粗心的Bug,JSON格式不规范导致AJAX错误
一.事件回放 今天工作时碰到了一个奇怪的问题,这个问题很早很早以前也碰到过,不过没想到过这么久了竟然又栽在这里. 当时正在联调一个项目,由于后端没有提供数据接口,于是我直接本地建立了一个 json ...
- JSON.parse()和JSON.stringify()
1.parse 用于从一个字符串中解析出json 对象.例如 var str='{"name":"cpf","age":"23&q ...
- json与JavaScript对象互换
1,json字符串转化为JavaScript对象: 方法:JSON.parse(string) eg:var account = '{"name":"jaytan&quo ...
- .NET平台开源项目速览(18)C#平台JSON实体类生成器JSON C# Class Generator
去年,我在一篇文章用原始方法解析复杂字符串,json一定要用JsonMapper么?中介绍了简单的JSON解析的问题,那种方法在当时的环境是非常方便的,因为不需要生成实体类,结构很容易解析.但随着业务 ...
- WebApi接口 - 响应输出xml和json
格式化数据这东西,主要看需要的运用场景,今天和大家分享的是webapi格式化数据,这里面的例子主要是输出json和xml的格式数据,测试用例很接近实际常用情况:希望大家喜欢,也希望各位多多扫码支持和点 ...
随机推荐
- lumen添加自定义异常
在公用工具类写异常类 <?php namespace Brady\Tool\Exception; use Brady\Tool\Constant\ErrorMsg; use \Exception ...
- PAT_B1013
这道题就是一道打印素数表的题目,本人使用的是筛选法,用bool数组记录是否为素数,每一次筛掉本轮数字的倍数,如果当前数字bool数组对应位置为false,则为素数. 这道题的坑是:你不知道最大第100 ...
- 安全学习概览——恶意软件分析、web渗透、漏洞利用和挖掘、内网渗透、IoT安全分析、区块链、黑灰产对抗
1 基础知识1.1 网络熟悉常见网络协议:https://www.ietf.org/standards/rfcs/1.2 操作系统1.3 编程2 恶意软件分析2.1 分类2.1.1 木马2.1.2 B ...
- 【迭代博弈+搜索+剪枝】poj-1568--Find the Winning Move
poj 1568:Find the Winning Move [迭代博弈+搜索+剪枝] 题面省略... Input The input contains one or more test cas ...
- 【邻接矩阵+基础BFS】PAT-L3-008. 喊山
L3-008. 喊山 一个山头呼喊的声音可以被临近的山头同时听到.题目假设每个山头最多有两个能听到它的临近山头.给定任意一个发出原始信号的山头,本题请你找出这个信号最远能传达到的地方.[何为最远?大致 ...
- webpack4 打包优化
1 参考文章 彻底解决 webpack 打包文件体积过大 webpack4提升180%编译速度 详解webpack4之splitchunksPlugin代码包分拆 webpack v4 中的断舍离 开 ...
- JavaScript 廖
=============== JavaScript代码可以直接嵌在网页的任何地方,不过通常我们都把JavaScript代码放到<head>中 ======注释 // 以双斜杠开头直到行末 ...
- LOJ2823 三个朋友 ——查询字串的哈希值
概念 查询字串的hash值 我们所说的哈希通常都是进制哈希,因为它具有一些很方便的性质,例如,具有和前缀和类似的性质. 假设一个字符串的前缀哈希值记为 $h[i]$,进制为 $base$,那么显然 $ ...
- 基于jQuery制作的手风琴折叠菜单
初始化为全部隐藏 点第一个,显示第一个所隐藏的内容 当点第二个的时候,第一个的内容隐藏,第二个栏目的内容显示,以此类推 下面是代码部分 <!DOCTYPE html><html la ...
- selenium之python源码解读-WebDriverWait
一.显示等待 所谓显示等待,是针对某一个特定的元素设置等待时间,如果在规定的时间内找到了该元素,就执行相关的操作,如果在规定的时间内没有找到该元素,在抛出异常 PS:注意显示等待和隐身等待的区别,隐身 ...