1. 介绍
JSON 是一种轻量级的数据交换格式,常用作前后端数据交换,Go 在 encoding/json
包中提供了对 JSON 的支持。
2. 用法
2.1 struct 序列化成
把 Go struct 序列化成 JSON 对象,Go 提供了 Marshal
方法,正如其含义所示表示编排序列化,函数签名如下:
func Marshal(v interface{}) ([]byte, error)
第一种写法
package main
import (
"encoding/json"
"fmt"
)
type Message struct {
Name string
Body string
Time int64
}
func main() {
m := Message{"Liming", "Hello", 1294706395881547}
b, err := json.Marshal(m)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(b))
}
$ go run json1.go
{"Name":"Liming","Body":"Hello","Time":1294706395881547}
第二种写法
package main
import (
"encoding/json"
"fmt"
)
type Product struct {
Name string
ProductID int64
Number int
Price float64
IsOnSale bool
}
func main() {
p := &Product{}
p.Name = "apple 8P"
p.IsOnSale = true
p.Number = 1000
p.Price = 4499.00
p.ProductID = 1
data, _ := json.Marshal(p)
fmt.Println(string(data))
}
$ go run json2.go
{"Name":"apple 8P","ProductID":1,"Number":1000,"Price":4499,"IsOnSale":true}
在 Go 中并不是所有的类型都能进行序列化:
- JSON object key 只支持 string
- Channel、complex、function 等 type 无法进行序列化
- 数据中如果存在循环引用,则不能进行序列化,因为序列化时会进行递归
- Pointer 序列化之后是其指向的值或者是 nil
还需要注意的是:只有 struct 中支持导出的 field 才能被 JSON package 序列化,即首字母大写的 field。
2.2 Struct Tag
Struct tag 可以决定 Marshal 和 Unmarshal 函数如何序列化和反序列化数据
package main
import (
"encoding/json"
"fmt"
)
type Product struct {
Name string `json:"name"`
ProductID int64 `json:"-"` // 表示不进行序列化
Number int `json:"number"`
Price float64 `json:"price"`
IsOnSale bool `json:"is_on_sale,string"`
}
func main() {
p := &Product{}
p.Name = "apple 8P"
p.IsOnSale = true
p.Number = 1000
p.Price = 4499.00
p.ProductID = 1
data, _ := json.Marshal(p)
fmt.Println(string(data))
}
$ go run json4.go
{"name":"apple 8P","number":1000,"price":4499,"is_on_sale":"true"}
omitempty
,tag里面加上omitempy,可以在序列化的时候忽略0值或者空值或者false。
package main
import (
"encoding/json"
"fmt"
)
type Product struct {
Name string `json:"name"`
ProductID int64 `json:"product_id,omitempty"`
Number int `json:"number"`
Price float64 `json:"price"`
IsOnSale bool `json:"is_on_sale,omitempty"`
}
func main() {
p := &Product{}
p.Name = "apple 8P"
p.IsOnSale = false
p.Number = 1000
p.Price = 4499.00
p.ProductID = 0
data, _ := json.Marshal(p)
fmt.Println(string(data))
}
$ go run json5.go
{"name":"apple 8P","number":1000,"price":4499}
2.3 struct反序列化
反序列化函数是 Unmarshal ,其函数签名如下:
func Unmarshal(data []byte, v interface{}) error
package main
import (
"encoding/json"
"fmt"
)
type Change struct {
Mid int //菜单Id
Actions []string //拥有的权限 "add" "view" "delete" "update"
}
type Change_slice struct {
ChgArr []Change //一个角色对应的菜单以及权限
}
func main() {
var str string = `{"ChgArr":[{"Mid":1,"Actions":["view","add"]},{"Mid":2,"Actions":["delete","add","update"]}]}`
var msgs Change_slice
err := json.Unmarshal([]byte(str), &msgs)
if err != nil {
fmt.Println("Can't decode json message", err)
} else {
fmt.Println(msgs.ChgArr[1].Mid)
}
}
$ go run json3.go
2
2.4 Tag反序列化
带标签(tag)的结构体(struct)
package main
import (
"encoding/json"
"fmt"
)
// Product _
type Product struct {
Name string `json:"name"`
ProductID int64 `json:"product_id,string"`
Number int `json:"number,string"`
Price float64 `json:"price,string"`
IsOnSale bool `json:"is_on_sale,string"`
}
func main() {
var data = `{"name":"apple 8P","product_id":"10","number":"10000","price":"6000","is_on_sale":"true"}`
p := &Product{}
err := json.Unmarshal([]byte(data), p)
if err != nil {
fmt.Println(err)
}
fmt.Println(*p)
}
$ go run json6.go
{apple 8P 10 10000 6000 true}
另外一种反序列化(格式)
var m Product
err := json.Unmarshal(b, &m)
2.5 interface{}反序列化
针对未知类型json
package main
import (
"encoding/json"
"fmt"
)
func main() {
a := `{"name":"chalvern.github.io","full_name":"chalvern/chalvern.github.io","private":false,"owner":{"login":"chalvern","html_url":"https://github.com/chalvern"},"html_url":"https://github.com/chalvern/chalvern.github.io","description":"jingwei.link blog"}`
var jingweiI interface{}
err := json.Unmarshal([]byte(a), &jingweiI)
if err != nil {
fmt.Println(err)
}
fmt.Printf("%#v \n %#v \n", jingweiI)
// 获取某个 key 的值
jingweiM, ok := jingweiI.(map[string]interface{})
if !ok {
fmt.Println("DO SOMETHING!")
return
}
fmt.Printf("%#v\n", jingweiM["name"])
// 获取嵌套的内容
owner, ok := jingweiM["owner"].(map[string]interface{})
if !ok {
fmt.Println("DO SOMETHING!")
return
}
fmt.Printf("%#v\n", owner["login"])
}
$ go run json8.go
map[string]interface {}{"description":"jingwei.link blog", "full_name":"chalvern/chalvern.github.io", "html_url":"https://github.com/chalvern/chalvern.github.io", "name":"chalvern.github.io", "owner":map[string]interface {}{"html_url":"https://github.com/chalvern", "login":"chalvern"}, "private":false}
%!v(MISSING)
"chalvern.github.io"
"chalvern"
2.6 编码解码
编码
json.NewEncoder(<Writer>).encode(v)
json.Marshal(&v)
解码
json.NewDecoder(<Reader>).decode(&v)
json.Unmarshal([]byte, &v)
除了 marshal 和 unmarshal 函数,Go 还提供了 Decoder 和 Encoder 对 stream JSON 进行处理,常见 request 中的 Body、文件等。
$ cat post.json
{
"name":"apple 8P",
"product_id":10,
"number":10000,
"price":6000,
"is_on_sale":"true"
}
$ cat json9.go
package main
import (
"encoding/json"
"fmt"
"io"
"os"
)
type Product struct {
Name string
ProductID int64
Number int
Price float64
IsOnSale bool
}
func main() {
jsonFile, err := os.Open("post.json")
if err != nil {
fmt.Println("Error opening json file:", err)
return
}
defer jsonFile.Close()
decoder := json.NewDecoder(jsonFile)
for {
var post Product
err := decoder.Decode(&post)
if err == io.EOF {
break
}
if err != nil {
fmt.Println("error decoding json:", err)
return
}
fmt.Println(post)
}
}
$ go run json9.go
{apple 8P 0 10000 6000 false}
3. 实战
3.1 编码解码比较
package main
import (
"encoding/json"
"fmt"
"bytes"
"strings"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
// 1. 使用 json.Marshal 编码
person1 := Person{"张三", 24}
bytes1, err := json.Marshal(&person1)
if err == nil {
// 返回的是字节数组 []byte
fmt.Println("json.Marshal 编码结果: ", string(bytes1))
}
// 2. 使用 json.Unmarshal 解码
str := `{"name":"李四","age":25}`
// json.Unmarshal 需要字节数组参数, 需要把字符串转为 []byte 类型
bytes2 := []byte(str) // 字符串转换为字节数组
var person2 Person // 用来接收解码后的结果
if json.Unmarshal(bytes2, &person2) == nil {
fmt.Println("json.Unmarshal 解码结果: ", person2.Name, person2.Age)
}
// 3. 使用 json.NewEncoder 编码
person3 := Person{"王五", 30}
// 编码结果暂存到 buffer
bytes3 := new(bytes.Buffer)
_ = json.NewEncoder(bytes3).Encode(person3)
if err == nil {
fmt.Print("json.NewEncoder 编码结果: ", string(bytes3.Bytes()))
}
// 4. 使用 json.NewDecoder 解码
str4 := `{"name":"赵六","age":28}`
var person4 Person
// 创建一个 string reader 作为参数
err = json.NewDecoder(strings.NewReader(str4)).Decode(&person4)
if err == nil {
fmt.Println("json.NewDecoder 解码结果: ", person4.Name, person4.Age)
}
}
结果:
json.Marshal 编码结果: {"name":"张三","age":24}
json.Unmarshal 解码结果: 李四 25
json.NewEncoder 编码结果: {"name":"王五","age":30}
json.NewDecoder 解码结果: 赵六 28
3.2 针对未知类型的json
package main
import (
"encoding/json"
"fmt"
)
func main() {
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`)
var f interface{}
json.Unmarshal(b, &f)
m := f.(map[string]interface{})
fmt.Println(m["Parents"]) // 读取 json 内容
fmt.Println(m["a"] == nil) // 判断键是否存在
}
$ go run json11.go
[Gomez Morticia]
true
参考连接:
https://learnku.com/go/t/23565/golang-json-coding-and-decoding-summary
https://jingwei.link/2019/03/15/golang-json-unmarshal-using.html
https://sanyuesha.com/2018/05/07/go-json/