🌙 go操作json
序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程。在序列化期间,对象将其当前状态写入到临时或持久性存储区。通过从存储区中读取对象的状态,重新创建该对象,则为反序列化。
简单地说把某种数据结构转为指定数据格式为“序列化”或“编码”(传输之前);而把“指定数据格式”转为某种数据结构则为“反序列化”或“解码”(传输之后)。
在Go语言中,encoding/json
标准包处理JSON数据的序列化与反序列化问题。
🌙 1.json序列化
json.Marshal
源码:
func Marshal(v interface{}) ([]byte, error) {
e := newEncodeState()
// 默认编码
err := e.marshal(v, encOpts{escapeHTML: true})
if err != nil {
return nil, err
}
buf := append([]byte(nil), e.Bytes()...)
encodeStatePool.Put(e)
return buf, nil
}
2
3
4
5
6
7
8
9
10
11
12
13
从上面的Marshal()
函数我们可以看到,数据结构序列化后返回的是字节数组,而字节数组很容易通过网络传输或写入文件存储。
而且在Go中,Marshal()
默认是设置escapeHTML = true
的,会自动把'<'
, '>'
, 以及 '&'
等转化为"\u003c"
, "\u003e"
以及 "\u0026"
。
func testMarshal() {
h := Human{"JEEK", "男", 18, Lesson{[]string{"Math", "Art"}}}
data, err := json.Marshal(&h)
if err != nil {
fmt.Println("json序列化失败", err)
return
}
fmt.Println("json序列化成功", string(data))
}
// json序列化成功 {"name":"JEEK","gender":"男","age":18,"lessons":["Math","Art"]}
2
3
4
5
6
7
8
9
10
11
🌙 2.json反序列化
json.Unmarshal
源码:
func Unmarshal(data []byte, v interface{}) error {
// Check for well-formedness.
// Avoids filling out half a data structure
// before discovering a JSON syntax error.
var d decodeState
err := checkValid(data, &d.scan)
if err != nil {
return err
}
d.init(data)
return d.unmarshal(v)
}
func checkValid(data []byte, scan *scanner) error {
scan.reset()
for _, c := range data {
scan.bytes++
if scan.step(scan, c) == scanError {
return scan.err
}
}
if scan.eof() == scanError {
return scan.err
}
return nil
}
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
从上面的UnMarshal()
函数我们可以看到,反序列化是读取字节数组,进而解析为对应的数据结构。
func testUnmarshal1() {
jsonStr := `{"name": "JEEK", "age": 18, "gender": "男", "lessons": ["English", "Art"], "room": 205, "test": null, "isAdult": true}`
var hu Human
err := json.Unmarshal([]byte(jsonStr), &hu)
if err != nil {
fmt.Println("json反序列化失败", err)
return
}
fmt.Println("json反序列化成功", hu)
}
// json反序列化成功 {JEEK 男 18 {[English Art]}}
2
3
4
5
6
7
8
9
10
11
12
13
- 反序列化任意JSON数据:
encoding/json
包使用 map[string]interface{}
和[]interface{}
储存任意的 JSON 对象和数组;其可以被反序列化为任何的 JSON blob 存储到接口值中。
直接使用 Unmarshal 把这个数据反序列化,并保存在map[string]interface{}
中,要访问这个数据,我们可以使用类型断言:
func testUnmarshal2() {
jsonStr := `{"name": "JEEK", "age": 18, "gender": "男", "lessons": ["English", "Art"], "room": 205, "test": null, "isAdult": true}`
var data map[string]interface{}
if err := json.Unmarshal([]byte(jsonStr), &data); err == nil {
fmt.Println("map结构")
fmt.Println(data)
}
for k, v := range data {
switch vv := v.(type) {
case string:
fmt.Println(k, "是string", vv)
case bool:
fmt.Println(k, "是bool", vv)
case float64:
fmt.Println(k, "是float64", vv)
case nil:
fmt.Println(k, "是nil", vv)
case []interface{}:
fmt.Println(k, "是array:")
for i, u := range vv {
fmt.Println(i, u)
}
default:
fmt.Println(k, "未知数据类型")
}
}
}
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
结果
map结构
map[age:18 gender:男 isAdult:true lessons:[English Art] name:JEEK room:205 test:<nil>]
name 是string JEEK
age 是float64 18
gender 是string 男
lessons 是array:
0 English
1 Art
room 是float64 205
test 是nil <nil>
isAdult 是bool true
Process finished with the exit code 0
2
3
4
5
6
7
8
9
10
11
12
13
14
🌙 3.json与go数据类型
json | go |
---|---|
object | map[string]interface{} |
array | []interface{} |
string | string |
boolean | bool |
number | float64 |
null | nill |
不是所有的数据都可以编码为 JSON 格式,只有验证通过的数据结构才能被编码:
- json 对象只支持字符串类型的 key;要编码一个 Go map 类型,map 必须是 map[string]T(T是 json 包中支持的任何类型)
- channel,复杂类型和函数类型不能被编码
- 不支持循环数据结构;它将引起序列化进入一个无限循环
- 指针可以被编码,实际上是对指针指向的值进行编码(或者指针是 nil)
🌙 4.结构体tag和json
Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。
结构体tag由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。
同一个结构体字段可以设置多个键值对tag,不同的键值对之间使用空格分隔。
type Person struct {
// 1.指定json序列化/反序列化时使用小写name
Name string `json:"name"`
Age int `json:"age"`
// 2.指定json序列化/反序列化时忽略此字段
Height float64 `json:"-"`
// 3.在tag中添加omitempty忽略空值
// 注意这里 hobby,omitempty 合起来是json tag值,中间用英文逗号分隔
Hobby []string `json:"hobby,omitempty"`
P Profile `json:"p"`
P1 Profile `json:"p1,omitempty"` // 不会忽略空值
// 4.忽略嵌套结构体空值字段,必须指针
P2 *Profile `json:"p2,omitempty"`
}
type Profile struct {
Website string `json:"site"`
Slogan string `json:"slogan"`
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
想要在嵌套的结构体为空值时,忽略该字段,仅添加omitempty
是不够的,还需要使用嵌套的结构体指针:
func testDemo() {
p := Person{
Name: "JEEK",
Age: 18,
Height: 175.6,
Hobby: []string{"Soccer", "Music"},
P: Profile{Website: "blog.zkkysqs.top", Slogan: "stay hungry, stay foolish"},
}
data, err := json.Marshal(p)
if err != nil {
fmt.Println("json parse err", err)
return
}
fmt.Println(string(data))
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
输出:
{
"name": "JEEK",
"age": 18,
"hobby": [
"Soccer",
"Music"
],
"p": {
"site": "blog.zkkysqs.top",
"slogan": "stay hungry, stay foolish"
},
"p1": {
"site": "",
"slogan": ""
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16