Go 学习笔记(20)— Go 操作 json 文件(编码生成 json、解码 json 为 map、解码 json 为 struct)
1. Json 概述
Go
语言对于标准格式的编码和解码都有良好的支持,由标准库中的 encoding/json
、 encoding/xml
、 encoding/asn1
等包提供支持并且这类包都有着相似的 API
接口。
json
类型有数字(十进制或科学记数法)、布尔值( true
或 false
)、字符串,其中字符串是以 双引号
包含的 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% 兼容原生库,但是性能超级好,预先缓存了对应 struct
的 decoder
实例,然后 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)相关推荐
- Liunx学习笔记 - 07 - 02 正则表达式与文件格式化处理
Liunx学习笔记 - 07 - 02 正则表达式与文件格式化处理 1 前言:啥是正则表达式 简单来讲,正则表达式是处理字符串的方法,它是以行为单位来进行字符串的处理行为,正则表达式通过一些特殊符号的 ...
- 小猫爪:S32K3学习笔记20:S32K3之SCST及其应用
小猫爪:S32K3学习笔记20:S32K3之SCST及其应用 1 前言 2 SCST简介 3 移植 4 应用 4.1 启动测试 4.2 使能FPU相关测试项 4.3 49号测试项中的MPU配置 4.4 ...
- tensorflow学习笔记——使用TensorFlow操作MNIST数据(1)
续集请点击我:tensorflow学习笔记--使用TensorFlow操作MNIST数据(2) 本节开始学习使用tensorflow教程,当然从最简单的MNIST开始.这怎么说呢,就好比编程入门有He ...
- Hadoop学习笔记—20.网站日志分析项目案例(一)项目介绍
Hadoop学习笔记-20.网站日志分析项目案例(一)项目介绍 网站日志分析项目案例(一)项目介绍:当前页面 网站日志分析项目案例(二)数据清洗:http://www.cnblogs.com/edis ...
- C# 学习笔记(12)hex文件转bin文件小工具
C# 学习笔记(12)hex文件转bin文件小工具 hex文件格式 hex文件格式网上有很多 我这里参考HEX文件格式详解https://blog.csdn.net/weixin_39752827/a ...
- jQuery学习笔记之DOM操作、事件绑定(2)
jQuery学习笔记之DOM操作.事件绑定(2) --------------------学习目录------------------------ 4.DOM操作 5.事件绑定 源码地址: https ...
- 2020-4-22 深度学习笔记20 - 深度生成模型 5 (有向生成网络--sigmoid信念网络/可微生成器网络/变分自编码器VAE/生产对抗网络GAN/生成矩匹配网络)
第二十章 深度生成模型 Deep Generative Models 中文 英文 2020-4-17 深度学习笔记20 - 深度生成模型 1 (玻尔兹曼机,受限玻尔兹曼机RBM) 2020-4-18 ...
- Linux学习笔记(七):文件压缩、打包与备份
Linux学习笔记(七):文件压缩.打包与备份 常见的压缩指令 gzip, zcat/zmore/zless/zgrep bzip2, bzcat/bzmore/bzless/bzgrep xz, x ...
- 编译原理学习笔记20——符号表
编译原理学习笔记20--符号表 20.1 符号表的组织与操作 20.2 符号表的内容 20.3 利用符号表分析名字的作用域 20.1 符号表的组织与操作 符号表 符号表的作用与组织 符号表的整理和查找 ...
- oracle rac 环境配置文件,学习笔记:Oracle RAC spfile参数文件配置案例详解
天萃荷净 rac中的spfile探讨,记录一下Oracle RAC搭建完成后关于spfile参数文件的配置案例,与更改RAC环境中参数文件的方法 今天朋友的的rac,因为被同事做数据库升级,分别在两个 ...
最新文章
- python排名上升_Python在TIOBE Index排名或將取代Java成為第2名
- 是否可以将标志传递给Gulp以使其以不同方式运行任务?
- c语言随机生成int64_t类型的数据_手把手教你代码生成(上):MATLAB代码生成
- 笔记本电脑与漂亮老婆
- python点名代码_基于python tkinter的点名小程序功能的实例代码
- 对pca降维后的手写体数字图片数据分类_机器学习:数据的准备和探索——特征提取和降维...
- Heroku和Java –从新手到初学者,第1部分
- do语句转化为局部函数一例
- ssh服务器拒绝了密码 请再试一次 Xftp5连接失败
- 图谱实战 | 面向C端场景的概念图谱构成、建设与应用索引
- java SSL 简单操作demo
- ***php调试总结
- JavaSE基础——Java多线程
- ***编程DIY (Delphi版) - 第2篇 单实例运行
- ddr3ddr4 lpddr4速率_LPDDR3内存就比DDR4差?是时候冷静一下了
- Word2016任意页开始设置页码
- MarkDown 内部跳转链接
- 软考有哪些实质性的用处?
- C++primer plus学习笔记
- 物联网漏洞挖掘入门--DLINK-DIR-645路由器栈溢出漏洞分析复现
热门文章
- golang通过RSA算法生成token,go从配置文件中注入密钥文件,go从文件中读取密钥文件,go RSA算法下token生成与解析;go java token共用
- 利用dom4j将实体类转换为对应的xml报文
- Docker入门六部曲——服务
- Go 学习笔记(80)— Go 标准库 container/list(单链表、双链表)
- 2022-2028年中国塑料人造革行业市场研究及前瞻分析报告
- HA: InfinityStones靶机渗透测试
- Python Re 模块超全解读!详细
- GAAFET与FinFET架构
- SECTIONS bss段初始化分析
- ARM的突破:超级计算机和Mac