JSON 是一种数据格式描述语言。以 key 和 value 构成的哈系结构,类似 Javascript 中的对象,python 中的字典。通常 json 格式的 key 是字符串,其值可以是任意类型,字串,数字,数组或者对象结构。更多关于 Json 的可以访问 JSON 了解。

数据结构 map

json 源于 Javascript 的对象结构,golang 中有直接对应的数据结构 map,可是 golang 的 map 也是 key-value 结构,同时 struct 结构体也可以描述 json。当然,对于 json 的数据类型,go 也会有对象的结构所匹配。大致对应关系如下:

数据类型 JSON Golang
字串 string string
整数 number int64
浮点数 number flaot64
数组 arrary slice
对象 object struct
布尔 bool bool
空值 null nil

JSON 编码

基本结构编码

golang 提供了 encoding/json 的标准库用于编码 json。大致需要两步:

  1. 首先定义 json 结构体。
  2. 使用 Marshal 方法序列化。

定义结构体的时候,只有字段名是大写的,才会被编码到 json 当中。

type Account struct {
Email string
password string
Money float64
} func main() {
account := Account{
Email: "phpgo@163.com",
password: "123456",
Money: 100.5,
} rs, err := json.Marshal(account)
if err != nil{
log.Fatalln(err)
} fmt.Println(rs)
fmt.Println(string(rs))
}

可以看到输出如下,Marshal() 方法接受一个空接口的参数,返回一个 []byte 结构。小写命名的 password 字段没有被编码到 json 当中,生成的 json 结构字段和 Account 结构一致。

[123 34 69 109 97 105 108 34 58 34 114 115 106 50 49 55 64 103 109 97 105 108 46 99 111 109 34 44 34 77 111 110 101 121 34 58 49 48 48 46 53 125]
{"Email":"phpgo@163.com","Money":100.5}

复合结构编码

相比字串,数字等基本数据结构,slice 切片,map 字典则是复合结构。这些结构编码也类似。不过 map 的 key 必须是字符串,而 value 必须是同一类型的数据。

type User struct {
Name string
Age int
Roles []string
Skill map[string]float64
} func main() { skill := make(map[string]float64) skill["python"] = 99.5
skill["elixir"] = 90
skill["ruby"] = 80.0 user := User{
Name:"rsj217",
Age: 27,
Roles: []string{"Owner", "Master"},
Skill: skill,
} rs, err := json.Marshal(user)
if err != nil{
log.Fatalln(err)
}
fmt.Println(string(rs))
}

输出:

{
"Name":"rsj217",
"Age":27,
"Roles":[
"Owner",
"Master"
],
"Skill":{
"elixir":90,
"python":99.5,
"ruby":80
}
}

嵌套编码

slice 和 map 可以匹配 json 的数组和对象,当然,前提是对象的 value 是同类型的情况。更通用的做法,对象的 key 可以是 string,但是其值可以是多种结构。golang 可以通过定义结构体来实现这种构造:

type User struct {
Name string
Age int
Roles []string
Skill map[string]float64
Account Account
} type Account struct {
Email string
Money float64
} func main() {
skill := map[string]float64{
"elixir":90,
"python":99.5,
"ruby":80,
}
account := Account{
Email: "phpgo@163.com",
Money: 102.3,
} user := User{
Name: "wenjianbao",
Age: 30,
Roles: []string{"Owner", "Master"},
Skill: skill,
Account: account,
} js, err := json.Marshal(user)
if err != nil {
log.Fatal(err)
} fmt.Println(string(js))
}

输出:

{
"Name":"wenjianbao",
"Age":30,
"Roles":[
"Owner",
"Master"
],
"Skill":{
"elixir":90,
"python":99.5,
"ruby":80
},
"Account":{
"Email":"phpgo@163.com",
"Money":102.3
}
}

通过定义嵌套的结构体 Account,实现了 key 与 value 不一样的结构。golang 的数组或切片,其类型也是一样的,如果遇到不同数据类型的数组,则需要借助空结构来实现:

type User struct {
Name string
Age int
Roles []string
Skill map[string]float64
Account Account
Extra []interface{}
} type Account struct {
Email string
Money float64
} func main() {
skill := map[string]float64{
"elixir": 90,
"python": 99.5,
"ruby": 80,
}
account := Account{
Email: "phpgo@163.com",
Money: 102.3,
}
extra := []interface{}{123, "Hello World"} user := User{
Name: "wenjianbao",
Age: 30,
Roles: []string{"Owner", "Master"},
Skill: skill,
Account: account,
Extra: extra,
} js, err := json.Marshal(user)
if err != nil {
log.Fatal(err)
} fmt.Println(string(js))
}

输出:

{
"Name":"wenjianbao",
"Age":30,
"Roles":[
"Owner",
"Master"
],
"Skill":{
"elixir":90,
"python":99.5,
"ruby":80
},
"Account":{
"Email":"phpgo@163.com",
"Money":102.3
},
"Extra":[
123,
"Hello World"
]
}

使用空接口,也可以定义像结构体实现那种不同 value 类型的字典结构。当空接口没有初始化其值的时候,零值是 nil。编码成 json 就是 null

type User struct {
Name string
Age int
Roles []string
Skill map[string]float64
Account Account Extra []interface{}
Level map[string]interface{}
} type Account struct {
Email string
Money float64
} func main() {
skill := map[string]float64{
"elixir": 90,
"python": 99.5,
"ruby": 80,
}
account := Account{
Email: "phpgo@163.com",
Money: 102.3,
} level := make(map[string]interface{})
level["web"] = "Good"
level["server"] = 90
level["tool"] = nil user := User{
Name: "wenjianbao",
Age: 30,
Roles: []string{"Owner", "Master"},
Skill: skill,
Account: account,
Level: level,
} js, err := json.Marshal(user)
if err != nil {
log.Fatal(err)
} fmt.Println(string(js))
}

输出:

{
"Name":"wenjianbao",
"Age":30,
"Roles":[
"Owner",
"Master"
],
"Skill":{
"elixir":90,
"python":99.5,
"ruby":80
},
"Account":{
"Email":"phpgo@163.com",
"Money":102.3
},
"Extra":null,
"Level":{
"server":90,
"tool":null,
"web":"Good"
}
}

可以看到 Extra 返回的并不是一个空的切片,而是 null。同时 Level 字段实现了向字典的嵌套结构。

StructTag 字段重名

通过上面的例子,我们看到了 Level 字段中的 key server等是小写字母,其他的都是大写字母。因为我们在定义结构的时候,只有使用大写字母开头的字段才会被导出。而通常 json 世界中,更盛行小写字母的方式。看起来就成了一个矛盾。其实不然,golang 提供了 struct tag 的方式可以重命名结构字段的输出形式。

type Account struct {
Email string `json:"email"`
Password string `json:"pass_word"`
Money float64 `json:"money"`
} func main() {
account := Account{
Email: "phpgo@163.com",
Password: "123456",
Money: 100.5,
} rs, err := json.Marshal(account)
if err != nil {
log.Fatal(err)
} fmt.Println(string(rs))
}

我们使用 struct tag,重新给 Aaccount 结构的字段进行了重命名。其中 email 小写了,并且password 字段还使用了下划线,输出的结果如下:

{
"email":"phpgo@163.com",
"pass_word":"123456",
"money":100.5
}

- 忽略字段

重命名的可以一个利器,这个利器还提供了更高级的选项。通常使用 marshal 的时候,会把结构体的所有除了私有字段都编码到 json,而实际开发中,我们定义的结构可能更通用,我们需要某个字段可以导出,但是又不能编码到 json 中。

此时使用 struact tag 的 - 符号就能完美解决,我们已经知道 _ 常用于忽略字段的占位,在 tag 中则使用短横线 - 。

type Account struct {
Email string `json:"email"`
Password string `json:"-"`
Money float64 `json:"money"`
} func main() {
account := Account{
Email: "phpgo@163.com",
Password: "123456",
Money: 100.5,
} rs, err := json.Marshal(account)
if err != nil {
log.Fatal(err)
} fmt.Println(string(rs))
}

输出:

{
"email":"phpgo@163.com",
"money":100.5
}

可见即使 Password 不是私有字段,因为 忽略了它,因此没有被编码到 json 输出。

omitempty 可选字段

对于另外一种字段,当其有值的时候就输出,而没有值(零值)的时候就不输出,则可以使用另外一种选项 omitempty

type Account struct {
Email string `json:"email"`
Password string `json:"password,omitempty"`
Money float64 `json:"money"`
} func main() {
account := Account{
Email: "phpgo@163.com",
Password: "",
Money: 100.5,
} rs, err := json.Marshal(account)
if err != nil {
log.Fatal(err)
} fmt.Println(string(rs))
}

输出:

{
"email":"phpgo@163.com",
"money":100.5
}

此时 password 不会被编码到 json 输出中。

string选项

golang 是静态类型语言,对于类型定义的是不能动态修改。在 json 处理当中,struct tag 的 string 可以起到部分动态类型的效果。有时候输出的 json 希望是数字的字符串,而定义的字段是数字类型,那么就可以使用 string 选项。

type Account struct {
Email string `json:"email"`
Password string `json:"password,omitempty"`
Money float64 `json:"money,string"`
} func main() {
account := Account{
Email: "phpgo@163.com",
Password: "123",
Money: 100.5,
} rs, err := json.Marshal(account)
if err != nil {
log.Fatal(err)
} fmt.Println(string(rs))
}

输出:

{
"email":"phpgo@163.com",
"password":"123",
"money":"100.5"
}

可以看到输出为 money: "100.5", money 字段的值是字串。(其实能转换成 100.50 会比转换成 100.5 更好,可是我没有找到通过 tag 的方式实现 :()。

总结

上面所介绍的大致覆盖了 golang 的 json 编码处理。总体原则分两步,首先定义需要编码的结构,然后调用 encoding/json 标准库的 Marshal 方法生成 json byte 数组,转换成 string 类型即可。

golang 和 json 的大部分数据结构匹配,对于复合结构,go 可以借助结构体和空接口实现 json 的数组和对象结构。通过 struct tag 可以灵活的修改 json 编码的字段名和输出控制。

相关文章

Golang 处理 Json(一):编码

Golang 处理 Json(二):解码

参考:

http://json.org/

http://www.jianshu.com/p/f3c2105bd06b

https://golang.org/pkg/encoding/json/

Golang 处理 Json(一):编码的更多相关文章

  1. Golang - 处理json

    目录 Golang - 处理json 1. 编码json 2. 解码json Golang - 处理json 1. 编码json 使用json.Marshal()函数可以对一组数据进行JSON格式的编 ...

  2. 48 【golang】json的效率

    本文将主要做如下几方面的测试: 1,构造一个[100]struct的数组,然后来测试它的json编码后的字符串 或者([]byte),首先关心它的功能是否正常: 2,在很早之前,我们在使用golang ...

  3. Golang 处理 Json(二):解码

    golang 编码 json 还比较简单,而解析 json 则非常蛋疼.不像 PHP 一句 json_decode() 就能搞定.之前项目开发中,为了兼容不同客户端的需求,请求的 content-ty ...

  4. Golang的json包

    encoding/json encoding/json是官方提供的标准json, 实现RFC 7159中定义的JSON编码和解码.使用的时候需要预定义struct,原理是通过reflection和in ...

  5. Go_14:GoLang中 json、map、struct 之间的相互转化

    1. golang 中 json 转 struct <1. 使用 json.Unmarshal 时,结构体的每一项必须是导出项(import field).也就是说结构体的 key 对应的首字母 ...

  6. GoLang中 json、map、struct 之间的相互转化

    1. golang 中 json 转 struct <1. 使用 json.Unmarshal 时,结构体的每一项必须是导出项(import field).也就是说结构体的 key 对应的首字母 ...

  7. golang解析json报错:invalid character '\x00' after top-level value

    golang解析json报错:invalid character '\x00' after top-level value 手动复制字符串:{"files":["c:/t ...

  8. golang webservice[ json Martini webframe]

    golang webservice[ json Martini webframe] https://github.com/brunoga/go-webservice-sample 自己修改了一下例子, ...

  9. Golang解析json的几种方法

    Golang解析json的几种方法 概要 使用Golang调用其它平台API接口时总会被多层的json串给恶心到,我记录一下自己解析json的几种方法. 一.自带的json包 func JsonUnm ...

随机推荐

  1. Spark笔记之使用UDAF(User Defined Aggregate Function)

    一.UDAF简介 先解释一下什么是UDAF(User Defined Aggregate Function),即用户定义的聚合函数,聚合函数和普通函数的区别是什么呢,普通函数是接受一行输入产生一个输出 ...

  2. Linux 黑白界面显示

    2014年1月14日 15:47:47 不知道别人怎么看,反正我觉得黑白配显示很方便阅读 命令: ls 脚本: ~/.bashrc 指令: alias ls='ls --color=never' 命令 ...

  3. 使用配置文件启动MongoDB

    Ubuntu 16.04 (阿里云ECS),MongoDB 4.0, 原来,已经写了10篇MongoDB的随笔了.可是,自己居然没有使用配置文件启动过MongoDB,对其更多的配置是不明白的. 昨天( ...

  4. 详解PHP的执行原理和流程

    简介 先看看下面这个过程: • 我们从未手动开启过PHP的相关进程,它是随着Apache的启动而运行的: • PHP通过mod_php5.so模块和Apache相连(具体说来是SAPI,即服务器应用程 ...

  5. LeetCode(22):括号生成

    Medium! 题目描述: 给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合. 例如,给出 n = 3,生成结果为: [ "((()))", ...

  6. supervisor安装部署和使用实例

    Supervisord是用Python实现的一款非常实用的进程管理工具,类似于monit,monit和supervisord的一个比较大的差异是supervisord管理的进程必须由superviso ...

  7. hdu 2545 求当前结点到根节点的距离

    求当前结点到根节点的距离 Sample Input 2 1 //n m 1 2 1 2 //询问 5 2 1 2 1 3 3 4 3 5 4 2 //询问 4 5 0 0 Sample Output ...

  8. 数学之美——HMM模型(二)解码和Forward算法

    上一篇讨论了HMM的基本概念和一些性质,HMM在现实中还是比较常见的,因此也带来一了一系列的HMM应用问题.HMM应用主要面向三个方面:预测.解码和学习.这篇主要讨论预测. 简单来说,预测就是给定HM ...

  9. 权限管理UI

    vue+vuex+vue-router+EF的权限管理系统 演示网站 首先说下这个项目吧.如标题一样是基于VUE+.NET开发的框架,也是群友一直吼吼吼要一个vue版本的ABP框架.我们先来看看首页吧 ...

  10. 【noip模拟赛3】拣钱

    描述 最近,Henry由于失恋(被某大牛甩掉!)心情很是郁闷.所以,他去了大牛家,寻求Michael大牛的帮助,让他尽快从失恋的痛苦中解脱出来.Michael大牛知道Henry是很爱钱的,所以他是费尽 ...