前两天在 dashboard 项目上有人上报了一个 bug,当设置一个值为默认值时,该字段会被忽略,看了一下代码,发现是定义结构体的时候使用了 tag omitempty

Tag

tag 是 golang 的一种对结构体做额外处理的方式。而其中的 omitempty 通常被用来省略掉空的字段。

我们知道,go 里面的类型都有默认值,int 的默认值为 0,string 的默认值是 “”,如果你定义了一个结构体,类似于下面这样:

1
2
3
4
type Dog struct {
Breed string
WeightKg int
}

此时生成一个 Dog 的实例,其中的 Breed 和 WeightKg 将会被赋值为默认值。那么考虑一个场景:在某个字段没有被赋值的时候,我不需要它出现在结构体的实例中,也就是说没有被赋值的字段将不存在。

如果要实现上面所说的效果,就需要使用到 omitempty 标签,同时要使用指针。如下

1
2
3
4
type A struct {
Breed string
WeightKg *int `json:",omitempty"`
}

验证

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

原理

go 支持通过反射来获取结构体中的标签,所以这里的 omitempty 其实也是通过反射来获取的,解析过程参考 Json Marshal 解析

注意

在结构体嵌套的时候,单纯的设置 omitempty 可能会出现问题。因为 go 只认识内置的基础类型,为了避免出现设置了 omitempty 之后,仍然输出默认值的情况,应该在定义结构体时使用指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type dimension struct {
Height int
Width int
}

type Dog struct {
Breed string
WeightKg int
Size dimension `json:",omitempty"`
}

func main() {
d := Dog{
Breed: "pug",
}
b, _ := json.Marshal(d)
fmt.Println(string(b))
}

如果存在一个结构体 Dog,Dog 里面包含了结构体 dimension,这时候单单设置一个 omietempty,是不能解决默认值的问题,因为结构体嵌套的时候,实际编译的时候还是会使用嵌套的结构体内部的变量,所以还是会设置成默认值,如果要解决这个问题,应该要设置成指针,指针的默认值为 nil,这样在解析 json 数据的时候就会忽略掉这个字段了。

总结

在使用 encoding/json 包时,想要在解析的时候忽略某个字段,可以加上 omitempty 标签,配合使用指针,可以达到忽略某个字段的效果。