Go
的标准包 encoding/json
实现了json对象
的编码和解码。
https://pkg.go.dev/json
1、介绍
1.1、Go和JSON数据类型
JSON |
Go |
Boolean |
bool |
Number |
float64 |
String |
string |
Array |
[]interface{} |
Object |
map[string]interface{} |
NULL |
nil |
1.2、结构体序列化规则
@注意:struct 可导出的字段(首字母大写),才能参与Json的序列化
标签 |
json的key |
有标签,json:"xx" |
key=xx |
无标签 |
key=结构体原属性字段 |
有标签,json:"-" |
会被忽略,不参与序列化 |
有标签,json:"xxx,omitempty" |
代表该字段为空值时,会被忽略。其中xxx 可以省略,, 不可以省略。 如:json:",omitempty" |
有标签,json:"xxx,string" |
代表输出类型会转化为字符串。其中xxx 也可以省略。它只适用于字符串、浮点数、整数类型的字段 |
a. 代码示例
1 2 3 4 5 6 7 8
| type Student struct {
Name string `json:"name"` Age int Home string `json:"-"` Phone string `json:"phone,omitempty"` Score float64 `json:"score,string"` }
|
b. 运行效果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| func TestMarshal(t *testing.T) { student1 := Student{ Name: "张三", Age: 18, Home: "北京", Score: 90.5, Phone: "", } json1, _ := json.Marshal(student1) fmt.Printf("序列化json:%s\n", json1) }
|
1.3、核心函数
1.3.1 Marshal
1
| func Marshal(v any) ([]byte, error)
|
将struct编码成json,可以接收任意类型
- 布尔型转换为 JSON 后仍是布尔型,如true -> true
- 浮点型和整数型转换后为JSON里面的常规数字,如 1.23 -> 1.23
- 字符串将以UTF-8编码转化输出为Unicode字符集的字符串,特殊字符比如<将会被转义为\u003c
- 数组和切片被转换为JSON 里面的数组,[]byte类会被转换为base64编码后的字符串,slice的零值被转换为null
- 结构体会转化为JSON对象,并且只有结构体里边以大写字母开头的可被导出的字段才会被转化输出,而这些可导出的字段会作为JSON对象的字符串索引
- 转化一个map 类型的数据结构时,该数据的类型必须是 map[string]T(T 可以是encoding/json 包支持的任意数据类型)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| package main
import ( "encoding/json" "fmt" )
type Person struct { Name string Age int Email string }
func main() { p := Person{ Name: "zhangsan", Age: 20, Email: "zhangsan@mail.com", } b, _ := json.Marshal(p) fmt.Printf("b: %v\n", string(b)) }
|
1.3.2 Unmarshal
1
| func Unmarshal(data []byte, v any) error
|
将json转码为struct结构体
这个函数会把传入的 data 作为一个JSON来进行解析,解析后的数据存储在参数 v 中。这个参数 v 也是任意类型的参数(但一定是一个类型的指针),原因是我们在是以此函数进行JSON 解析的时候,这个函数不知道这个传入参数的具体类型,所以它需要接收所有的类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package main
import ( "encoding/json" "fmt" )
type Person struct { Name string Age int Email string }
func main() { b := []byte(`{"Name":"zhangsan","Age":20,"Email":"zhangsan@mail.com"}`) var m Person json.Unmarshal(b, &m) fmt.Printf("m: %v\n", m) }
|
2、匿名字段序列化
2.1、无JSON
标签
a. 字段标签不重复
School.Name和StudentA.Name,Json标签不一致。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| type School struct { Name string `json:"schoolName"` Address string `json:"schoolAddress"` }
type StudentA struct { Name string `json:"name"` School }
func TestAnonymousTagDifferent(t *testing.T) { var student = StudentA{ Name: "小明", School:School{ Name: "北京大学", Address: "北京海淀区", }, } jsonByte, _ := json.Marshal(student) fmt.Printf("json: %s \n",jsonByte) }
|
b. 字段标签重复
Class.Name和StudentB.Name,Json标签一致,都是 json:"name"
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| type StudentB struct { Name string `json:"name"` Class }
type Class struct { Name string `json:"name"` Desc string `json:"desc"` }
func TestAnonymousTagSame(t *testing.T) { var student = StudentB{ Name: "小明", Class: Class{ Name: "高二(1)班", Desc: "优秀班级", }, } jsonByte, _ := json.Marshal(student) fmt.Printf("json: %s \n", jsonByte) }
|
根据上面代码,得知如果字段标签冲突,冲突的匿名字段会被忽略。
2.2、有JSON
标签
当匿名字段设置json
标签时, 就不会出现冲突的情况,因为序列化后的匿名字段会变成对象。
a. 字段标签重复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| type Bird struct { Name string `json:"name"` Category `json:"category"` } type Category struct { Name string `json:"name"` }
func TestAnonymousWithTag(t *testing.T) { b := Bird{ Name: "喜鹊", Category:Category{Name: "鸟类"}, } jsonByte, _ := json.Marshal(b) fmt.Printf("json: %s \n", jsonByte) }
|
b. 字段标签不重复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| type Bird struct { Name string `json:"name"` Category `json:"category"` } type Category struct { Name string `json:"categoryName"` }
func TestAnonymousWithTag(t *testing.T) { b := Bird{ Name: "喜鹊", Category:Category{Name: "鸟类"}, } jsonByte, _ := json.Marshal(b) fmt.Printf("json: %s \n", jsonByte) }
|
3、匿名字段反序列化
3.1、无JSON
标签
a. 字段标签不重复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| type BookClass struct { Name string `json:"className"` Company string `json:"company"` }
type GoStudy struct { Name string `json:"name"` Desc string `json:"desc"` BookClass }
func TestUnMarshal(t *testing.T) { jsonStr := `{"name":"Go语言高级编程","desc":"这是一本Go学习书籍","className":"IT行业书籍","company":"xxx出版社"}` var goStudy GoStudy err := json.Unmarshal([]byte(jsonStr), &goStudy) if err != nil { t.Error(err) return } fmt.Printf("反序列化结果: %+v\n",goStudy) }
|
b. 字段标签重复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| type BookClass struct { Name string `json:"name"` Company string `json:"company"` } type GoStudy struct { Name string `json:"name"` Desc string `json:"desc"` BookClass } func TestUnMarshal(t *testing.T) { jsonStr := `{"desc":"这是一本Go学习书籍","company":"xxx出版社","name":"IT行业书籍"}` var goStudy GoStudy err := json.Unmarshal([]byte(jsonStr), &goStudy) if err != nil { t.Error(err) return } fmt.Printf("反序列化结果: %+v\n",goStudy) }
|
@注意: 当字段标签重复时,反序列化会优先给主属性字段赋值。
3.2、有JSON
标签
a. 字段标签重复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| type BookClass struct { Name string `json:"name"` Company string `json:"company"` } type GoStudy struct { Name string `json:"name"` Desc string `json:"desc"` BookClass `json:"bookClass"` } func TestUnMarshal(t *testing.T) { jsonStr1 := `{"name":"Go语言高级编程","desc":"这是一本Go学习书籍","company":"xxx出版社"}` jsonStr2 := `{"name":"Go语言高级编程","desc":"这是一本Go学习书籍","bookClass":{"name":"IT行业书籍","company":"xxx出版社"}}` var res1 GoStudy var res2 GoStudy _ = json.Unmarshal([]byte(jsonStr1), &res1) _ = json.Unmarshal([]byte(jsonStr2), &res2) fmt.Printf("res1结果: %+v\n", res1) fmt.Printf("res2结果: %+v\n", res2) }
|
b. 字段标签不重复
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| type BookClass struct { Name string `json:"bookName"` Company string `json:"company"` } type GoStudy struct { Name string `json:"name"` Desc string `json:"desc"` BookClass `json:"bookClass"` } func TestUnMarshal(t *testing.T) { jsonStr1 := `{"bookName":"Go语言高级编程","desc":"这是一本Go学习书籍","company":"xxx出版社"}` jsonStr2 := `{"name":"Go语言高级编程","desc":"这是一本Go学习书籍","bookClass":{"bookName":"IT行业书籍","company":"xxx出版社"}}` var res1 GoStudy var res2 GoStudy _ = json.Unmarshal([]byte(jsonStr1), &res1) _ = json.Unmarshal([]byte(jsonStr2), &res2) fmt.Printf("res1结果: %+v\n", res1) fmt.Printf("res2结果: %+v\n", res2) }
|
4、匿名字段json
总结
4.1、序列化
a. 匿名字段无标签
当匿名字段没有指定标签时,序列化后的结构为同级,如{"..":"..",..}
当匿名属性和主属性的字段标签一样时,序列化会忽略匿名属性的字段。
当匿名属性和主属性的字段标签不一样时,序列化不忽略任何字段。
b. 匿名字段有标签
- 当匿名字段
a
指定标签时,序列化后的结构为上下级,如{"..":"..","a":{"xx":"xx"}}
- 匿名属性和主属性字段,标签无论是否一致,序列化都不会忽略任何字段。
4.2、反序列化
a. 匿名字段无标签
- 当匿名字段没有指定标签时,可解析的
JSON
结构,为: {"..":"..",..}
- 当匿名属性和主属性的字段标签一样时,会优先将值赋给主属性,匿名属性为类型零值。
- 当匿名属性和主属性的字段标签不一样时,会正常解析。
b. 匿名字段有标签
- 当匿名字段指定标签时,可解析的
JSON
结构,为: {"..":"..","a":{"xx":"xx"}}
- 匿名属性和主属性字段,标签无论是否一致,反序列化都能正常赋值。
当结构体中嵌套匿名结构体字段时,在进行序列化和反序列时,推荐为匿名字段加上json标签。