1. Json 概述

Go 语言对于标准格式的编码和解码都有良好的支持,由标准库中的 encoding/jsonencoding/xmlencoding/asn1 等包提供支持并且这类包都有着相似的 API 接口。

json 类型有数字(十进制或科学记数法)、布尔值( truefalse)、字符串,其中字符串是以 双引号 包含的 Unicode 字符序列。

基础类型可以通过 json 的数组和对象类型进行递归组合:

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

标准库 encoding/json 解析 JSON 有两种方式:

  • 根据 JSON 内容格式定义对应的结构体(struct)
  • 使用 map[string]interface{} 加载 JSON 数据。

实际工作中,个人不建议采用根据 JSON 内容格式定义对应的结构体(struct),当 JSON 内容格式过于复杂的时候,对应的结构体(struct)的定义过程会变得复杂,这样会增加大量的代码。

因此建议使用 map[string]interface{} 加载 JSON 数据。

2. 代码示例

package mainimport ("encoding/json""fmt""log"
)// Movie struct
type Movie struct { // 只有导出的结构体成员才会被编码,所以此处 Movie 用大写字母表示Title  stringYear   int  `json:"released"`Color  bool `json:"color,omitempty"`Actors []string
}var movies = []Movie{{Title: "中国机长", Year: 2019, Color: true, Actors: []string{"张涵予", "袁泉"}},{Title: "攀登者", Year: 2019, Color: true, Actors: []string{"吴京", "章子怡"}},
}func main() {jsonStr, err := json.MarshalIndent(movies, "", "    ")if err != nil {log.Fatal(err)return}fmt.Printf("%s\n", jsonStr)}

输出:

  [{"Title": "中国机长","released": 2019,"color": true,"Actors": ["张涵予","袁泉"]},{"Title": "攀登者","released": 2019,"color": true,"Actors": ["吴京","章子怡"]}]

在编码时,默认使用 Go 语言结构体的成员名字作为 JSON 的对象。只有导出的结构体成员才会被编码,这也就是我们为什么选择用大写字母开头的成员名称。

type Movie struct { // 只有导出的结构体成员才会被编码,所以此处 Movie 用大写字母表示

其中 Year 名字的成员在编码后变成了 released ,还有 Color 成员编码后变成了小写字母开头的 color 。这是因为结构体成员 Tag 所导致的。

{"Title": "中国机长","released": 2019,"color": true,"Actors": ["张涵予","袁泉"]}

结构体的成员 Tag 可以是任意的字符串面值,但是通常是一系列用空格分隔的 key:"value" 键值对序列;因为值中含有双引号字符,因此成员 Tag 一般用原生字符串面值的形式书写。

json 开头键名对应的值用于控制 encoding/json 包的编码和解码的行为,并且 encoding/... 下面其它的包也遵循这个约定。

Year   int  `json:"released"`

Color 成员的 Tag 还带了一个额外的 omitempty 选项,表示当 Go 语言结构体成员为空或零值时不生成该 JSON 对象(这里false为零值)。如果 【攀登者】 是一个黑白电影,就不会输出 Color 成员。

Color  bool `json:"color,omitempty"`

编码的逆操作是解码,对应将 JSON 数据解码为 Go 语言的数据结构, Go 语言中一般叫 unmarshaling ,通过 json.Unmarshal 函数完成。下面的代码将 JSON 格式的电影数据解码为一个结构体 slice ,结构体中只有 Title 成员。

通过定义合适的 Go 语言数据结构,我们可以选择性地解码 json 中感兴趣的成员。当 Unmarshal 函数调用返回, slice 将被只含有 Title 信息的值填充,其它 JSON 成员将被忽略。

package mainimport ("encoding/json""fmt""log"
)// Movie struct
/*
注意
1. json 编解码 只能处理结构体成员变量是大写的字段,小写的直接忽略
2. 如果在编码时 想让json字符串的字段小写,那么需要再结构体中加Tag如果再解码时, json有小写的字段名,那么在定义结构体接收的时候,成员名要大写,然后也要加Tag*/
type Movie struct { // 只有导出的结构体成员才会被编码,所以此处 Movie 用大写字母表示Title  stringYear   int  `json:"released"`Color  bool `json:"color,omitempty"`Actors []string
}var movies = []Movie{{Title: "中国机长", Year: 2019, Color: true, Actors: []string{"张涵予", "袁泉"}},{Title: "攀登者", Year: 2019, Color: true, Actors: []string{"吴京", "章子怡"}},
}func main() {// 编码将 go 结构体转换成 json 字符串jsonData, err := json.MarshalIndent(movies, "", " ")if err != nil {log.Fatal(err)return}fmt.Printf("jsonData is %s\n", jsonData)fmt.Println("-----------------")//解码,将json字符串 转换成 go数据类型var titles []struct {Title string}if err := json.Unmarshal(jsonData, &titles); err != nil {log.Fatal(err)return}fmt.Printf("%+v\n", titles)var years []struct {Year int `json:"released"`}if err := json.Unmarshal(jsonData, &years); err != nil {log.Fatal(err)return}fmt.Printf("%+v\n", years)var actors []struct {Actors []string}if err := json.Unmarshal(jsonData, &actors); err != nil {log.Fatal(err)return}fmt.Printf("%+v\n", actors)fmt.Println("-----------------")var myMovies []struct {Title  stringYear   int `json:"released"`Actors []string}if err := json.Unmarshal(jsonData, &myMovies); err != nil {log.Fatal(err)return}fmt.Printf("%+v\n", myMovies)
}

输出结果:

jsonData is [{"Title": "中国机长","released": 2019,"color": true,"Actors": ["张涵予","袁泉"]},{"Title": "攀登者","released": 2019,"color": true,"Actors": ["吴京","章子怡"]}
]
-----------------
[{Title:中国机长} {Title:攀登者}]
[{Year:2019} {Year:2019}]
[{Actors:[张涵予 袁泉]} {Actors:[吴京 章子怡]}]
-----------------
[{Title:中国机长 Year:2019 Actors:[张涵予 袁泉]} {Title:攀登者 Year:2019 Actors:[吴京 章子怡]}]

3. 编码生成 json

使用 json 包的 MarshalIndent 函数进行编码。这个函数可以很方便地将 Go 语言的 map 类型的值或者结构类型的值转换为易读格式的 JSON 文档。

序列化( marshal)是指将数据转换为 JSON字符串的过程。

// 这个示例程序展示如何序列化JSON字符串
package mainimport ("encoding/json""fmt""log"
)func main() {// 创建一个保存键值对的映射c := make(map[string]interface{})c["Name"] = "wohu"c["Year"] = "2019"c["Contact"] = map[string]interface{}{"home": "12345678","cell": "110110",}// 将这个映射序列化到JSON字符串data, err := json.MarshalIndent(c, "", "  ")if err != nil {log.Println("ERROR:", err)return}fmt.Println(string(data))
}

输出:

{"Contact": {"cell": "110110","home": "12345678"},"Name": "wohu","Year": "2019"
}

函数 MarshalIndent 返回一个 byte 切片,用来保存 JSON 字符串和一个 error值。

// MarshalIndent很像Marshal,只是用缩进对输出进行格式化
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error) {

MarshalIndent函数里再一次看到使用了空接口类型 interface{}。函数 MarshalIndent 会使用反射来确定如何将 map 类型转换为 JSON 字符串。

4. 解码 json 为 map

JSON 数据里边的元素类型将做如下转换:

  • JSON 中的布尔值将会转换为 Go 中的 bool 类型;
  • 数值会被转换为 Go 中的 float64 类型;
  • 字符串转换后还是 string 类型;
  • JSON 数组会转换为 []interface{} 类型;
  • JSON 对象会转换为 map[string]interface{} 类型;
  • null 值会转换为 nil

4.1 解码 json 字符串

可以将 JSON 文档解码到一个 map 变量中。

// 这个示例程序展示如何解码JSON字符串
package mainimport ("encoding/json""fmt""log"
)// JSON 包含要反序列化的样例字符串var JSON = `{"name": "wohu","year": "2020","contact": {"home": "12345678","cell": "110110"}
}`func main() {// 将JSON字符串反序列化到map变量// 变量c声明为一个map类型,其键是string类型,其值是interface{}类型。// 这意味着这个map类型可以使用任意类型的值作为给定键的值。var c map[string]interface{}err := json.Unmarshal([]byte(JSON), &c)if err != nil {log.Println("ERROR:", err)return}fmt.Println("Name:", c["name"])fmt.Println("Year:", c["year"])fmt.Println("Contact")// 因为每个键的值的类型都是interface{},所以必须将值转换为合适的类型,才能处理这个值。// 下面展示了如何将contact键的值转换为另一个键是string类型,值是interface{}类型的map类型。fmt.Println("H:", c["contact"].(map[string]interface{})["home"])fmt.Println("C:", c["contact"].(map[string]interface{})["cell"])
}

输出:

Name: wohu
Year: 2020
Contact
H: 12345678
C: 110110

4.2 解码 json 文件

demo.json 内容:

{"name": "wohu","year": "2020","contact": {"home": "12345678","cell": "110110"}
}

代码:

package mainimport ("encoding/json""fmt""io/ioutil"
)func main() {content, _ := ioutil.ReadFile("demo.json")r := make(map[string]interface{})json.Unmarshal([]byte(content), &r)fmt.Println("r['name'] is ", r["name"])
}

5. 解码 json 为 struct

package mainimport ("encoding/json""fmt""io/ioutil"
)type Contact struct {Home string `json:"home"`Cell string `json:"cell"`
}type Student struct {Name    string  `json:"name"`Year    string  `json:"year"`Contact Contact `json:"contact"`
}// 全局 单例模式
var Gjson *Studentfunc display() {fmt.Println("student.name is ", Gjson.Name)fmt.Println("student.Contact.Home is ", Gjson.Contact.Home)fmt.Println("student.Contact.Cell is ", Gjson.Contact.Cell)
}func main() {content, _ := ioutil.ReadFile("demo.json")var student Studentjson.Unmarshal(content, &student)// 执行以下代码之后,后面就可以使用全局的 Gjson 来获取配置文件中的字段Gjson = &studentdisplay()
}

6. 三方库

除此之外,Go 的第三方包也支持 JSON 数据的解析,我们以 jsoniter 为例。第三包 jsoniter 是 100% 兼容原生库,但是性能超级好,预先缓存了对应 structdecoder 实例,然后 unsafe.Pointer 省掉了一些 interface{} 的开销,还有一些文本解析上的优化,并且它的使用方式与标准库 encoding/json 存在相似。

安装第三包 jsoniter 命令 :

go get -v github.com/json-iterator/go

示例代码

package mainimport ("fmt""io/ioutil"jsoniter "github.com/json-iterator/go"
)func main() {content, _ := ioutil.ReadFile("demo.json")r := make(map[string]interface{})json := jsoniter.ConfigCompatibleWithStandardLibraryjson.Unmarshal([]byte(content), &r)fmt.Println("r['name'] is ", r["name"])}
 r := make(map[string]interface{})fmt.Println([]byte(result))// 调用标准库encoding/json的Unmarshal// 将JSON数据(JSON以字符串形式表示)转换成[]byte,并将数据加载到对象r的内存地址json.Unmarshal([]byte(result), &r)// r["data"]是读取JSON最外层的key// 如果嵌套JSON数据,则使用map[string]interface{}读取下一层的JSON数据// 如读取key为data里面嵌套的result:r["data"].(map[string]interface{})["result"]// 如果JSON的某个key的数据以数组表示,则使用([]interface{})[index]读取数组中某个数据。// 如读取key为result的第四个数据:r["data"].(map[string]interface{})["result"].([]interface{})[3]fmt.Println(r["data"].(map[string]interface{})["result"].([]interface{})[3])

Go 学习笔记(20)— Go 操作 json 文件(编码生成 json、解码 json 为 map、解码 json 为 struct)相关推荐

  1. Liunx学习笔记 - 07 - 02 正则表达式与文件格式化处理

    Liunx学习笔记 - 07 - 02 正则表达式与文件格式化处理 1 前言:啥是正则表达式 简单来讲,正则表达式是处理字符串的方法,它是以行为单位来进行字符串的处理行为,正则表达式通过一些特殊符号的 ...

  2. 小猫爪:S32K3学习笔记20:S32K3之SCST及其应用

    小猫爪:S32K3学习笔记20:S32K3之SCST及其应用 1 前言 2 SCST简介 3 移植 4 应用 4.1 启动测试 4.2 使能FPU相关测试项 4.3 49号测试项中的MPU配置 4.4 ...

  3. tensorflow学习笔记——使用TensorFlow操作MNIST数据(1)

    续集请点击我:tensorflow学习笔记--使用TensorFlow操作MNIST数据(2) 本节开始学习使用tensorflow教程,当然从最简单的MNIST开始.这怎么说呢,就好比编程入门有He ...

  4. Hadoop学习笔记—20.网站日志分析项目案例(一)项目介绍

    Hadoop学习笔记-20.网站日志分析项目案例(一)项目介绍 网站日志分析项目案例(一)项目介绍:当前页面 网站日志分析项目案例(二)数据清洗:http://www.cnblogs.com/edis ...

  5. C# 学习笔记(12)hex文件转bin文件小工具

    C# 学习笔记(12)hex文件转bin文件小工具 hex文件格式 hex文件格式网上有很多 我这里参考HEX文件格式详解https://blog.csdn.net/weixin_39752827/a ...

  6. jQuery学习笔记之DOM操作、事件绑定(2)

    jQuery学习笔记之DOM操作.事件绑定(2) --------------------学习目录------------------------ 4.DOM操作 5.事件绑定 源码地址: https ...

  7. 2020-4-22 深度学习笔记20 - 深度生成模型 5 (有向生成网络--sigmoid信念网络/可微生成器网络/变分自编码器VAE/生产对抗网络GAN/生成矩匹配网络)

    第二十章 深度生成模型 Deep Generative Models 中文 英文 2020-4-17 深度学习笔记20 - 深度生成模型 1 (玻尔兹曼机,受限玻尔兹曼机RBM) 2020-4-18 ...

  8. Linux学习笔记(七):文件压缩、打包与备份

    Linux学习笔记(七):文件压缩.打包与备份 常见的压缩指令 gzip, zcat/zmore/zless/zgrep bzip2, bzcat/bzmore/bzless/bzgrep xz, x ...

  9. 编译原理学习笔记20——符号表

    编译原理学习笔记20--符号表 20.1 符号表的组织与操作 20.2 符号表的内容 20.3 利用符号表分析名字的作用域 20.1 符号表的组织与操作 符号表 符号表的作用与组织 符号表的整理和查找 ...

  10. oracle rac 环境配置文件,学习笔记:Oracle RAC spfile参数文件配置案例详解

    天萃荷净 rac中的spfile探讨,记录一下Oracle RAC搭建完成后关于spfile参数文件的配置案例,与更改RAC环境中参数文件的方法 今天朋友的的rac,因为被同事做数据库升级,分别在两个 ...

最新文章

  1. python排名上升_Python在TIOBE Index排名或將取代Java成為第2名
  2. 是否可以将标志传递给Gulp以使其以不同方式运行任务?
  3. c语言随机生成int64_t类型的数据_手把手教你代码生成(上):MATLAB代码生成
  4. 笔记本电脑与漂亮老婆
  5. python点名代码_基于python tkinter的点名小程序功能的实例代码
  6. 对pca降维后的手写体数字图片数据分类_机器学习:数据的准备和探索——特征提取和降维...
  7. Heroku和Java –从新手到初学者,第1部分
  8. do语句转化为局部函数一例
  9. ssh服务器拒绝了密码 请再试一次 Xftp5连接失败
  10. 图谱实战 | 面向C端场景的概念图谱构成、建设与应用索引
  11. java SSL 简单操作demo
  12. ***php调试总结
  13. JavaSE基础——Java多线程
  14. ***编程DIY (Delphi版) - 第2篇 单实例运行
  15. ddr3ddr4 lpddr4速率_LPDDR3内存就比DDR4差?是时候冷静一下了
  16. Word2016任意页开始设置页码
  17. MarkDown 内部跳转链接
  18. 软考有哪些实质性的用处?
  19. C++primer plus学习笔记
  20. 物联网漏洞挖掘入门--DLINK-DIR-645路由器栈溢出漏洞分析复现

热门文章

  1. golang通过RSA算法生成token,go从配置文件中注入密钥文件,go从文件中读取密钥文件,go RSA算法下token生成与解析;go java token共用
  2. 利用dom4j将实体类转换为对应的xml报文
  3. Docker入门六部曲——服务
  4. Go 学习笔记(80)— Go 标准库 container/list(单链表、双链表)
  5. 2022-2028年中国塑料人造革行业市场研究及前瞻分析报告
  6. HA: InfinityStones靶机渗透测试
  7. Python Re 模块超全解读!详细
  8. GAAFET与FinFET架构
  9. SECTIONS bss段初始化分析
  10. ARM的突破:超级计算机和Mac