0%

golang - json操作的注意点

变量的大小写

go中根据首字母的大小写来确定可以访问的权限。如果首字母大写,则可以被其他的包访问;如果首字母小写,则只能在本包中使用。包括接口、类型、函数和变量等。

可以简单的理解成,首字母大写是公有的,首字母小写是私有的

下面是一个排查该问题的例子:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package main

import (
"encoding/json"
"fmt"
)

type Position struct {
X int
Y int
Z int
}

type Student struct {
Name string
Sex string
Age int
position Position
}

func main() {
position1 := Position{10, 20, 30}
student1 := Student{"zhangsan", "male", 20, position1}
position2 := Position{15, 10, 20}
student2 := Student{"lisi", "female", 18, position2}

var srcSlice = make([]Student, 2)
srcSlice[0] = student1
srcSlice[1] = student2
fmt.Printf("Init:srcSlice is : %v\n", srcSlice)
data, err := json.Marshal(srcSlice)
if err != nil {
fmt.Printf("Serialize:json.Marshal error! %v\n", err)
return
}
fmt.Println("After serialize, data : \n", string(data))

var dstSliece = make([]Student, 2)
err = json.Unmarshal(data, &dstSliece)
if err != nil {
fmt.Printf("Deserialize: json.Unmarshal error! %v\n", err)
return
}
fmt.Printf("Deserialize:dstSlice is : %v\n", dstSliece)
}

很意外的是,我们反序列化后获取的对象数据dstSliece是错误的,Position里的数据都变成了0。而json.Unmarshal没有返回任何异常。

观察将序列化后的json串,Position的数据丢了,这使得我们想到了可见性,即大写的符号在包外可见。通过走查代码,我们发现Student的定义中,Position的变量名是小写开始的。

1
2
3
4
5
6
7
type Student struct {
Name string
Sex string
Age int
//position Position
Position Position
}

改成大写后再观察结果,可以正常序列化。

序列化到json后改成小写

对于json串,很多人喜欢全小写,对于大写开头的key感觉很刺眼,我们继续改进:

1
2
3
4
5
6
7
8
9
10
11
12
type Position struct {
X int `json:"x"`
Y int `json:"y"`
Z int `json:"z"`
}

type Student struct {
Name string `json:"name"`
Sex string `json:"sex"`
Age int `json:"age"`
Posi Position `json:"position"`
}

再次运行程序,结果是我们期望的,打印如下:

1
2
3
4
Init:srcSlice is : [{zhangsan male 20 {10 20 30}} {lisi female 18 {15 10 20}}]
After serialize, data :
[{"name":"zhangsan","sex":"male","age":20,"position":{"x":10,"y":20,"z":30}},{"name":"lisi","sex":"female","age":18,"position":{"x":15,"y":10,"z":20}}]
Deserialize:dstSlice is : [{zhangsan male 20 {10 20 30}} {lisi female 18 {15 10 20}}]

omitempty

1
2
3
4
type Dog struct {
Breed string
WeightKg int
}
1
2
3
4
5
6
7
8
func main() {
d := Dog{
Breed: "dalmation",
WeightKg: 45,
}
b, _ := json.Marshal(d)
fmt.Println(string(b))
}

output:

1
{"Breed":"dalmation","WeightKg":45}

如果对dog没有设置:

1
2
3
4
5
6
7
func main() {
d := Dog{
Breed: "pug",
}
b, _ := json.Marshal(d)
fmt.Println(string(b))
}

这会输出

1
{"Breed":"pug","WeightKg":0}

即使WeightKg的值是未知的。

更好的方式是使“WeightKg”为null, 或者根本没有这个值。

omitempty tag 正好可以起到这个作用.

1
2
3
4
5
type Dog struct {
Breed string
// The first comma below is to separate the name tag from the omitempty tag
WeightKg int `json:",omitempty"`
}

这会输出

1
{"Breed":"pug"}

参考链接

Golang初学者易犯的三种错误

Go’s “omitempty” explained