Go JSON序列化和反序列化

JSON格式

JSON语法检查网址: www.json.cn
如下所示,JSON的键值对的键必须以""双引号包裹,也就是key的类型必须是string,而val则随意。

[
    {
        "name":"小花",
        "age":"10",
        "adderss":"中国"
    },
    {
        "name":"小刘",
        "age":10,
        "address":[
            0,
            1
        ]
    }
]

序列化

encoding/json包

对上面的json数据,为了在go语言中构造,我们使用了[]map[string]interface这种slice,就能够得到上述一致的内容

package main

import (
	"encoding/json"
	"fmt"
)

func main(){
	jsonSlice:=make([]map[string]interface{},0)
	jsonSlice=append(jsonSlice,make(map[string]interface{}))
	jsonSlice[0]["name"]="小花"
	jsonSlice[0]["age"]="10"
	jsonSlice[0]["address"]="中国"
	jsonSlice=append(jsonSlice,make(map[string]interface{}))
	jsonSlice[1]["name"]="小刘"
	jsonSlice[1]["age"]=10
	jsonSlice[1]["address"]=[]int{0,1}
	jsonString,err:=json.Marshal(jsonSlice)
	if err!=nil{
		fmt.Println("error")
	}
	fmt.Println(string(jsonString))

}

输出

[{"address":"中国","age":"10","name":"小花"},{"address":[0,1],"age":10,"name":"小刘"}]

但是由于map可能存在不同的顺序(后文运行结果好像为字典序),所以输出顺序和上面json输出的不一样,因此我们可以使用结构体来构造,得到一致的顺序

注意

Stuct中只有导出字段(首字母大写的)才可以转换为json,未导出的会被忽略!!!

package main

import (
	"encoding/json"
	"fmt"
)
type person struct {
	Name string
	Age interface{}
	Address interface{}
	id string
}
func main(){
	jsonSlice:=make([]*person,0)
	jsonSlice=append(jsonSlice,&person{"小花","10","中国","12345"})
	jsonSlice=append(jsonSlice,&person{"小刘",10,[]int{0,1},"007"})
	jsonString,err:=json.Marshal(jsonSlice)
	if err!=nil{
		fmt.Println("error")
	}
	fmt.Println(string(jsonString))
}

输出

[{"Name":"小花","Age":"10","Address":"中国"},{"Name":"小刘","Age":10,"Address":[0,1]}]

如上输出,结构体里的id没有输出,我们可以把这个认为是私有的

序列化中key-val的对应关系

  • 普通类型

在对非key-value这种对应关系的变量,如int,float,序列化以后也就只是转字符串而已,并不会按照键值对那样对应,这样得到的结果是没有意义的

  • 结构体

把成员变量名当成key,成员变量的值当成val

使用tag来设置导出的key不是成员变量名

比如,中国人喜欢中文,那么将key设置为中文不是更容易理解

package main

import (
	"encoding/json"
	"fmt"
)
type person struct {
	Name string `json:"名字"`
	Age interface{} `json:"年龄"`
	Address interface{} `json:"地址"`
	id string
}
func main(){
	jsonSlice:=make([]*person,0)
	jsonSlice=append(jsonSlice,&person{"小花","10","中国","12345"})
	jsonSlice=append(jsonSlice,&person{"小刘",10,[]int{0,1},"007"})
	jsonString,err:=json.Marshal(jsonSlice)
	if err!=nil{
		fmt.Println("error")
	}
	fmt.Println(string(jsonString))
}

输出

[{"名字":"小花","年龄":"10","地址":"中国"},{"名字":"小刘","年龄":10,"地址":[0,1]}]
  • map

key-val一致

反序列化

将json字符串反序列化为go中的类型

仍然以开头的json字符串为例,分别反序列化为[]map[string]interface和[]struct

下面引入了两种map,看看分别转换的情况

package main

import (
	"encoding/json"
	"fmt"
)

func main(){
	jsonString:=`[{"name":"小花","age":"10","address":"中国"},{"name":"小刘","age":10,"address":[0,1]}]`
	var p1 []map[string]interface{}
	err:=json.Unmarshal([]byte(jsonString),&p1)
	if err!=nil{
		fmt.Println("p1:",err)
	}
	fmt.Println("p1:",p1)

	var p2 []map[int]interface{}
	err=json.Unmarshal([]byte(jsonString),&p2)
	if err!=nil{
		fmt.Println("p2:",err)
	}
	fmt.Println("p2:",p2)
}

输出

p1: [map[address:中国 age:10 name:小花] map[address:[0 1] age:10 name:小刘]]
p2: json: cannot unmarshal number name into Go value of type int
p2: [map[] map[]]

由此可见,map的key类型是必须固定的,固定为string类型,否则就会报错

下面分别引入了不同的结构体,通过输出说明什么样的结构体才能成功的反序列化

package main

import (
	"encoding/json"
	"fmt"
)

type person1 struct {
	Name string
	Age interface{}
	Address interface{}
}
type person2 struct {
	name string
	age interface{}
	address interface{}
}
type person3 struct {
	Naem string
	Age interface{}
	Address interface{}
}
type person4 struct {
	Name string
	Age interface{}
	Address interface{}
	Id string
}
type person5 struct {
	Name int
	Age interface{}
	Address interface{}
	id string
}
func main(){
	jsonString:=`[{"name":"小花","age":"10","address":"中国"},{"name":"小刘","age":10,"address":[0,1]}]`
	var p1 []person1
	err:=json.Unmarshal([]byte(jsonString),&p1)
	if err!=nil{
		fmt.Println("p1:",err)
	}
	fmt.Println("p1:",p1)

	var p2 []person2
	err=json.Unmarshal([]byte(jsonString),&p2)
	if err!=nil{
		fmt.Println("p2:",err)
	}
	fmt.Println("p2:",p2)

	var p3 []person3
	err=json.Unmarshal([]byte(jsonString),&p3)
	if err!=nil{
		fmt.Println("p3:",err)
	}
	fmt.Println("p3:",p3)

	var p4 []person4
	err=json.Unmarshal([]byte(jsonString),&p4)
	if err!=nil{
		fmt.Println("p4:",err)
	}
	fmt.Println("p4:",p4)

	var p5 []person5
	err=json.Unmarshal([]byte(jsonString),&p5)
	if err!=nil{
		fmt.Println("p5:",err)
	}
	fmt.Println("p5:",p5)
}

输出

p1: [{小花 10 中国} {小刘 10 [0 1]}]
p2: [{ <nil> <nil>} { <nil> <nil>}]
p3: [{小花 10 中国} {小刘 10 [0 1]}]
p31: [{小花 10 中国} {小刘 10 [0 1]}]
p4: [{小花 10 中国 } {小刘 10 [0 1] }]
p5: json: cannot unmarshal string into Go struct field person5.Name of type int
p5: [{0 10 中国 } {0 10 [0 1] }]

person1是绝对合法的,

person2未导出所以就不能反序列化成功,但是不会报错,

person3的Naem,导致一个key不匹配,所以就忽略这个字段,不报错

person31的Naem虽然不匹配,但是tag是匹配的,所以会使用这个字段进行匹配

person4多了一个字段,所以也忽略这个字段,不报错

person5的Name类型是int类型,和json字符串中的string类型是不符合的,会报错

总结

  1. 序列化和反序列化包为encoding/json
  2. 函数可以使用Marshal和Unmarshal
  3. 理论上go里面的所有类型都可以序列化,但是结构体,map序列化较有意义
    • 结构体的非导出字段不进行序列化
    • 结构体如果定义了tag,那么序列化的key就是tag
  4. 反序列化一定要注意字段的类型匹配,否则会报错;
    • 结构体的非导出字段不进行反序列化
    • 名称不匹配的结构体或结构体中多出的字段会忽略,不报错
    • 结构体中如果定义了tag,那么反序列化的key也会和tag进行匹配