=Start=
缘由:
现在的json格式用的非常之多,比如:配置文件,接口之间的数据传递。所以,用Go 语言来对json字符串进行读取、解析、在各类型之间进行转换是一个非常常见的问题,这里简单整理总结一下,方便以后用到的时候参考。
正文:
参考解答:
Go 语言在内置的”encoding/json”库中提供了Marshal和Unmarshal函数以支持(已知结构的)struct结构体和json字符串之间的转换:
// 将 struct结构体 转成 json格式串 func Marshal(v interface{}) ([]byte, error) // Marshal returns the JSON encoding of v. // 将 json格式串 转成 struct结构体 func Unmarshal(data []byte, v interface{}) error // Unmarshal parses the JSON-encoded data and stores the result in the value pointed to by v.
1、将固定格式的json字符串转换成struct结构体
来一个实际点的例子就是:通过接口 http://ipinfo.io/json 获取自己网络出口公网IP地址的相关信息如下:
{ "ip": "115.21.14.38", "city": "Xinghuo", "region": "Beijing", "country": "CN", "loc": "58.2500,36.1670", "org": "AS165256 CN CARE NETWORK LTD" }
然后你想从上面这段json字符串中解析出IP地址和地区region出来,因为返回内容的格式都是固定的,所以我们可以先对着内容定义一个结构体IpInfo出来,借助Unmarshal函数将json字符串解析到一个IpInfo结构体变量中,然后进行下一步的处理:
package main import ( "encoding/json" "log" ) // 在 tag 中定义的名称不需要首字母大写,且对应的 json 串中字段名称仍然大小写不敏感 type IpInfo struct { Ip string `json:"ip"` City string `json:"city"` Region string `json:"region"` Country string `json:"country"` Loc string `json:"loc"` Org string `json:"org"` } func main() { jsonStr := `{ "ip": "115.21.14.38", "city": "Xinghuo", "region": "Beijing", "country": "CN", "loc": "58.2500,36.1670", "org": "AS165256 CN CARE NETWORK LTD" }` log.Printf("jsonStr:\n%s\n", jsonStr) var ipInfo IpInfo json.Unmarshal([]byte(jsonStr), &ipInfo) log.Printf("[ipInfo]value: %v\ntype: %T\n", ipInfo, ipInfo) // type: main.IpInfo log.Println("ipInfo.Ip:", ipInfo.Ip) // log.Println("ipInfo.ip:", ipInfo.ip) // ipInfo.ip undefined }
2.将struct结构体变量转换成json字符串的形式
这里列出两个方法:
- json.NewEncoder(b).Encode(struct_var)
- b2, err := json.Marshal(struct_var)
package main import ( "bytes" "encoding/json" "log" ) type User struct { Name string Age uint32 Balance uint64 test int // 在 struct 中 key 是小写开头的项为「不可导出项」,因为只有大写的才是可对外提供访问的 } // 在 tag 中定义的名称不需要首字母大写,且对应的 json 串中字段名称仍然大小写不敏感 type Employee struct { Name string `json:"empname"` // tag标签 Number int `json:"empid"` } func main() { u := User{Name: "ixyzero.com", Balance: 8} b := new(bytes.Buffer) json.NewEncoder(b).Encode(u) // 方法一 log.Printf("[u]value: %v\ntype: %T\n\n", u, u) // type: main.User (可以看到test字段的内容) log.Printf("[b]value: %v\ntype: %T\n", b, b) // type: *bytes.Buffer user := User{Name: "ixyzero", Age: 20, Balance: 5, test: 111} b2, err := json.Marshal(user) // 方法二 if err != nil { log.Println(err) } log.Printf("[b2]value: %v\ntype: %T\n", b2, b2) // type: []uint8 str2 := string(b2) log.Printf("[str2]value: %v\ntype: %T\n", str2, str2) // type: string emp := &Employee{Name: "Rocky", Number: 5454} e, err := json.Marshal(emp) if err != nil { log.Println(err) } str2 = string(e) log.Printf("[str2]value: %v\ntype: %T\n", str2, str2) // type: string } /* 2018/10/27 14:52:02 [u]value: {ixyzero.com 0 8 0} type: main.User 2018/10/27 14:52:02 [b]value: {"Name":"ixyzero.com","Age":0,"Balance":8} type: *bytes.Buffer 2018/10/27 14:52:02 [b2]value: [123 34 78 97 109 101 34 58 34 105 120 121 122 101 114 111 34 44 34 65 103 101 34 58 50 48 44 34 66 97 108 97 110 99 101 34 58 53 125] type: []uint8 2018/10/27 14:52:02 [str2]value: {"Name":"ixyzero","Age":20,"Balance":5} type: string 2018/10/27 14:52:02 [str2]value: {"empname":"Rocky","empid":5454} type: string */
3.嵌套json字符串的解析
上面2种讲的其实都是格式比较简单的json字符串的处理,但实际场景中可能嵌套格式的json字符串会占多数,比如:
{ "args": {}, "data": "{\"title\":\"a breakfast\"}", "files": {}, "form": {}, "headers": { "Accept-Encoding": "gzip", "Connection": "close", "Content-Length": "23", "Content-Type": "application/json", "Cookie": "id=anyone", "Host": "httpbin.org", "User-Agent": "iPhone X" }, "json": { "title": "a breakfast" }, "origin": "137.21.83.79", "url": "https://httpbin.org/post" }
针对这种较为复杂的嵌套格式json字符串的处理,方法简单的来说有3种:
这里为了演示方便起见,先用gjson试试,其中nested.json文件的内容是上面的那段json字符串。
package main import ( "encoding/json" "fmt" "github.com/tidwall/gjson" "io/ioutil" "os" ) func typeof(v interface{}) string { switch v.(type) { case int: return "int" case float64: return "float64" case map[string]interface{}: return "map[string]interface {}" //... etc default: return "unknown" } } func main() { file, e := ioutil.ReadFile("./nested.json") if e != nil { fmt.Printf("File error: %v\n", e) os.Exit(1) } myJson := string(file) m, ok := gjson.Parse(myJson).Value().(map[string]interface{}) if !ok { fmt.Println("Error") } for key, item := range m { value_type := typeof(item) if value_type == "map[string]interface {}" { fmt.Printf("key: %v(%T), item: %v, type: %T\n", key, key, item, item) itemMap := item.(map[string]interface{}) // 要先进行一次类型转换(暂时还没搞清楚具体原因) fmt.Printf("key: %v(%T), itemMap: %v, type: %T\n", key, key, itemMap, itemMap) // for k, v := range item { // cannot range over item (type interface {}) for k, v := range itemMap { fmt.Printf("\tkey: %v(%T), value: %v, type: %T\n", k, k, v, v) } } else { fmt.Printf("key: %v(%T), value: %v, type: %T\n", key, key, item, item) } } fmt.Println() jsonBytes, err := json.Marshal(m) if err != nil { fmt.Println(err) } fmt.Println(string(jsonBytes)) }
参考链接:
- Go 语言中如何将一个 struct 结构体转换成 json 格式的字符串?
http://www.golangprograms.com/how-to-convert-go-struct-to-json.html - Go_14:GoLang中 json、map、struct 之间的相互转化#nice
- How To Correctly Serialize JSON String In Golang
- Unmarshaling nested JSON objects in Golang#nice
- Parsing Deeply Nested JSON in Go
- golang json数据的处理
http://ju.outofmemory.cn/entry/366846 - golang如何解析结构不确定的Json
- golang解析json格式
- Go实战–Golang Response Snippets: JSON, XML and more(http请求返回值)#nice
- Go实战–net/http中JSON的使用(The way to go)
=END=
《 “Go语言学习#11-如何解析json字符串” 》 有 6 条评论
Golang如何优雅地提取JSON数据中需要的字段数据
https://blog.csdn.net/yjp19871013/article/details/83035588
https://github.com/thedevsaddam/gojsonq
Golang结构体中Tag的使用
https://blog.csdn.net/yjp19871013/article/details/81258609
Go面试必考题目之method篇
https://mp.weixin.qq.com/s/US7MnIJfekJRazioxyWQhg
https://yourbasic.org/golang/pointer-vs-value-receiver/
https://golang.org/ref/spec#Method_sets
https://stackoverflow.com/questions/19433050/go-methods-sets-calling-method-for-pointer-type-t-with-receiver-t
Go 语言操作 JSON 的几种方法
https://morning.work/page/go/json.html
`
# 标准库的 json 模块
Go 语言标准库 encoding/json 提供了操作 JSON 的方法,一般可以使用 json.Marshal 和 json.Unmarshal 来序列化和解析 JSON 字符串:
# 更加灵活和更好性能的 jsoniter 模块
标准库 encoding/json 在使用时需要预先定义结构体,使用时显得不够灵活。这时候可以尝试使用 github.com/json-iterator/go 模块,其除了提供与标准库一样的接口之外,还提供了一系列更加灵活的操作方法。
# 另辟蹊径提高性能的 easyjson 模块
标准库 encoding/json 需要依赖反射来实现,因此性能上会比较差。 github.com/mailru/easyjson 则是利用 go generate 机制自动为结构体生成实现了 MarshalJSON 和 UnmarshalJSON 方法的代码,在序列化和解析时可以直接生成对应字段的 JSON 数据,而不需要运行时反射。据官方的介绍,其性能是标准库的 4~5 倍,是其他 json 模块的 2~3 倍。
## benchmark
从以上结果可以看到, jsoniter 在序列化和解析时均有比较好的性能, easyjson 次之,标准库 json 则在解析时性能比较差。当然,这并不是一个比较严格的性能测试,比如没有考虑内存分配问题以及多种不同的 JSON 结构和数据长度的测试。但是,如果综合考虑性能和灵活性, jsoniter 可能是一个不错的选择。
`
Golang 中 JSON 和结构体的组合使用
https://xanthusl.github.io/blog/2017/06/%E8%AF%91%20golang%20%E4%B8%AD%20json%20%E5%92%8C%E7%BB%93%E6%9E%84%E4%BD%93%E7%9A%84%E7%BB%84%E5%90%88%E4%BD%BF%E7%94%A8/
Golang 高性能 json 包:easyjson
https://gocn.vip/article/411
如何快速提升 Go 程序性能?
https://mp.weixin.qq.com/s/rta0Prdc5xq7Zd7mLzvcWA
使用go进行日志分析并生成excel,再也不担心做日志分析了
https://mp.weixin.qq.com/s/PJFE-fJ_pt60H9XZlxBNpw
https://xuri.me/excelize/zh-hans/
https://github.com/360EntSecGroup-Skylar/excelize
简化 Go 中对 JSON 的处理
https://mp.weixin.qq.com/s/ND_W1g0k0tKZNpC2ohDR0w
https://alanstorm.com/simplified-json-handling-in-go/
`
在强类型语言中,JSON 通常很难处理 —— JSON 类型有字符串、数字、字典和数组。如果你使用的语言是 javascript、python、ruby 或 PHP,那么 JSON 有一个很大的好处就是在解析和编码数据时你不需要考虑类型。
在强类型语言中,你需要自己去定义怎么处理 JSON 对象的字符串、数字、字典和数组。在 Go 语言中,你使用内建的 API 时需要考虑如何更好地把一个 JSON 文件转换成 Go 的数据结构。
解析/序列化为 map[string]interface
解析/序列化为 struct
SJSON 和 GJSON
Go 内建的 JSON 处理并没有变化,但是已经出现了一些成熟的旨在用起来更简洁高效的处理 JSON 的包。
SJSON(写 JSON)和 GJSON(读 JSON)是 Josh Baker 开发的两个包,你可以用来读写 JSON 字符串。
`
https://github.com/tidwall/sjson
https://github.com/tidwall/gjson