go的json序列化
前言:
go语言的json序列化与反序列化借助的go语言的数据结构是 结构体(python中借助的是字典)
声明一个Movie结构体
type Movie struct {
Title string
Year int `json:"released"`
Color bool `json:"color,omitempty"`
Actors []string
}
一、序列化(json.Marshal或json.MarshalIndent)
1、json.Marshal
// 先实例化一个结构体
var movies = []Movie{
{Title: "Casablanca", Year: 1942, Color: false,
Actors: []string{"Humphrey Bogart", "Ingrid Bergman"}},
{Title: "Cool Hand Luke", Year: 1967, Color: true,
Actors: []string{"Paul Newman"}},
{Title: "Bullitt", Year: 1968, Color: true,
Actors: []string{"Steve McQueen", "Jacqueline Bisset"}},
}
// struct -> json string 将结构体序列化为json 字符串
data, err := json.Marshal(movies)
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)
// out
/*
[{"Title":"Casablanca","released":1942,"Actors":["Humphrey Bogart","Ingrid Bergman"]},{"Title":"Cool Hand Luke","released":1967,"color":true,"Actors":["Paul Newman"]},{"Title":"Bullitt","released":1968,"color":true,"Actors":["Steve McQueen","Jacqueline Bisset"]}]
*/
Marshal 序列化后返回一个编码后的字节切片[]uint8,包含很长的字符串,没有空白缩进,这种紧凑的表现形式不便与阅读,所以使用MarshalIndent
2、json.MarshalIndent
data, err := json.MarshalIndent(movies, "", " ")
if err != nil {
log.Fatalf("JSON marshaling failed: %s", err)
}
fmt.Printf("%s\n", data)
// MarshalIndent(),需要三个参数,后两个额外的字符串参数 用于表示每一行输出的前缀和每一个层级的缩进,
输出:
[
{
"Title": "Casablanca",
"released": 1942,
"Actors": [
"Humphrey Bogart",
"Ingrid Bergman"
]
},
{
"Title": "Cool Hand Luke",
"released": 1967,
"color": true,
"Actors": [
"Paul Newman"
]
},
{
"Title": "Bullitt",
"released": 1968,
"color": true,
"Actors": [
"Steve McQueen",
"Jacqueline Bisset"
]
}
]
可能留意到了,json序列化编码后,原来的Year 和 Color 字段发生了变化,变成了released和color, 这是因为结构体成员 Tag导致的
Tag信息
Year int `json:"released"`
Color bool `json:"color,omitempty"`
反引号里的Tag可以是任意的字符串面值,通常是一系列用空格分割的key:“value”键值对序列,因为值种含有双引号字符,因此tag一般使用原生字符串面值的形式书写。json开头键名对应的值用于控制encoding/json包的编码和解码的行为,并且encoding/…下面其它的包也遵循这个约定。成员Tag中json对应值的第一部分用于指定JSON对象的名字,序列化的结果就是结构体的字段名变为了json对象的名字,如go结构体的Year字段对应到JSON中的released对象,Color成员的Tag还带了一个额外的omitempty选项,表示当go语言结构体成员为空或零值时不生成json对象,电影Casablanca是一个黑白电影,并没有输出color成员。
二、反序列化(json.Unmarshal)
m1 := Movie{}
json_str := `{
"Title": "Casablanca",
"released": 1942,
"color": true,
"Actors": [
"Humphrey Bogart",
"Ingrid Bergman"
]
}`
err = json.Unmarshal([]byte(json_str), &m1)
// 只反序列化部分字段,选择性的解码json对象中的成员
var released []struct{ Released int }// 自返回发行时间
var actor []struct{Actors []string}//只返回主演演员
var titles []struct{ Title string } // 返回电影title
var color []struct{ Color bool } // 返回电影颜色
err = json.Unmarshal(data, &titles)
err = json.Unmarshal(data, &released)
err = json.Unmarshal(data, &actor)
if err != nil {
log.Fatalf("JSON unmarshaling failed: %s", err)
}
//fmt.Println(titles)
fmt.Println(released)
//fmt.Println(color)
fmt.Println(actor)
fmt.Println(m1)
编码的逆操作是解码,对应将json数据解码为go语言的数据结构,go语言的反序列化通过json.unmarshal()函数完成。注意,在反序列Tag中的json对象的时候,字段的名字必须是json的对象字段并要将json对象字段的首字母大写,才能反序列化成功
三、嵌套结构体序列化
//嵌套结构体序列化
type User struct {
Name string `json:"name"`
Email string `json:"email,omitempty"`
Hobby []string `json:"hobby,omitempty"`
Profile
}
type Profile struct {
Website string `json:"site"`
Slogan string `json:"slogan"`
}
func nestedStructDemo() {
u1 := User{
Name: "bob",
Hobby: []string{"足球", "双色球"},
//Profile:profile,
}
b, err := json.MarshalIndent(u1,""," ")
if err != nil {
fmt.Printf("json.Marshal failed, err:%v\n", err)
return
}
fmt.Printf("str:%s\n", b)
}
// 序列化结果
/*
str:{
"name": "bob",
"hobby": [
"足球",
"双色球"
],
"site": "",
"slogan": ""
}
*/
1、变为双层嵌套的json串
type User struct {
Name string `json:"name"`
Email string `json:"email,omitempty"`
Hobby []string `json:"hobby,omitempty"`
Profile `json:"profile"`
}
// str:{"name":"bob","hobby":["足球","双色球"],"profile":{"site":"","slogan":""}}
2、忽略空值字段
type User struct {
Name string `json:"name"`
Email string `json:"email,omitempty"`
Hobby []string `json:"hobby,omitempty"`
Profile
}
// 给嵌套的结构体字段Tag增加 omiteempty 字段
type Profile struct {
Website string `json:"site,omitempty"`
Slogan string `json:"slogan,omitempty"`
}
// 序列化输出结果
/*
str:{
"name": "bob",
"hobby": [
"足球",
"双色球"
]
}
*/
// 或者
type User struct {
Name string `json:"name"`
Email string `json:"email,omitempty"`
Hobby []string `json:"hobby,omitempty"`
*Profile `json:"profile,omitempty"`
}
type Profile struct {
Website string `json:"site"`
Slogan string `json:"slogan"`
}
// str:{"name":"bob","hobby":["足球","双色球"]}
3、不修改原结构体忽略空值字段
type Usr struct {
Name string `json:"name"`
Password string `json:"password"`
}
type PublicUser struct {
*Usr
// 匿名嵌套
Password *struct{} `json:"password,omitempty"`
}
func omitPasswordDemo() {
u1 := Usr{Name:"bob",Password:"123"}
p := PublicUser{Usr:&u1}
u,_ := json.Marshal(p)
fmt.Printf("str:%s\n",u)
}
// 本质就是借助匿名嵌套,并添加了omitempty,当空值时不序列化
四、关于json tag字段的反序列化
- 关于字段可见性:序列化和反序列化本质上是两个包的数据之间的转化,一个是自己写的包,就是自己写的代码所在的包,另一个就是json包
而go语言的一个包之间的关键特性就是 类型变量首字母大写才能对外可见,所以在涉及序列化和反序列化时,必须将字段首字母大写 - 关于前后端交互:如果必须得用全小写,那么就使用 go语言的tag标签功能,tag的格式需严格执行:即 反引号``,将键值对扩起来,里面的值要用双引号扩起来,
如果是多个tag,之间用《空格》隔开。 - 反序列化的格式:json.Unmarshal([]byte(jsonStr),&c2),括号里的是byte类型的数组,和结构体的指针。
- 反序列化时,对于go结构体中有Tag对应的json对象的,存放反序列化的结构体中的字段必须为json对象的字段,并且首字母大写,不然无法实现反序列化。