前言
Golang的结构体标签可能每一个Gopher都在用,尤其是在json处理的地方用。比如:
1 | type NetConf struct { |
毋庸置疑,这个 NetConf 结构体实体变量在转换 json 的时候,Master 字段会变为 json 字符串中的 “master “。
如果你对结构体标签了解仅此不多,那可以继续往下看了。
从一个结构体标签可以从属多个字段开始说
1 | type T struct { |
上面代码在声明的时候,f4 和 f5 是一起进行的声明,因此后边的字段标签 “f four and five” 即是 f4 字段的,也是 f5 字段的。
聪明如你,你的问题就来了。
上面代码的字段标签比较特殊,是一个字符串 “f four and five” ,我们常用在 json 处理时的字段标签往往是这样的 json:”debug”` ,那么,同样的字段标签,用在多个字段上,是否可行?
我们验证一下这个问题:
1 | package main |
代码输出:
1 | {"master":"master1","mode":"mode1"} |
可以看到,同一个结构体中,多个字段拥有同一个字段标签后,golang 内置的 json 解析器将直接忽略这个字段标签对应的所有字段的解析。
换句话说,对于 golang 的 json 包,同样的字段标签,不能用在多个字段上。但是,json 包不能处理,并不等于 golang 不允许,这只是个别 package 的行为,仅此而已。
虽然我们在对 NetConf 的 MTU 和 Debug 字段都设置的标签为 test,我们怎么证明编译之后,这个字段标签确实存在呢(而不是被编译器主动忽略)?
这就引出我们下一个内容。
如何拿到结构体的标签内容
还是用上面的结构体,代码如下:
1 | package main |
输出:
1 | $ go run main.go |
说明什么,同一个结构体的不同字段,确实是可以有相同的字段标签的。只是 golang 内置的 json 解析器自己处理了这种特殊情况而已(或者说,json解析器自己认为这种情况对它而言有点特殊)。
其实,结构体标签是有常规格式和非常规格式的。而,golang 的 json 包,只是用了结构体标签的常规格式而已。
json:"master"
这种就是常规格式。除此之外就是非常规格式。下面,我们重点来说。
结构体标签的【常规格式】
上面已经举例了一个常规格式了,下面再举一个
1 | type T struct { |
可以看出来,这种具备一个个键值对形式组成的结构体标签,便是常规格式。
我们可以使用反射,获取结构体标签的每一个键值对内容
1 | package main |
1 | $ go run main.go |
可以看出来,用 reflect 包的 Tag ,可以获取每一个键值对的内容,以及是否存在这个键。
另外需要特别注意:结构体标签的多个键值对之间,必须用空格分割。,不能用逗号!!!,不能用逗号!!!,不能用逗号!!!
结构体标签的键值对中,键有什么用
键的用处,通常来说,就是 package name 。比如 json 解析的时候,结构体标签的键值对中,键通常就设置为 json。值设置为要解析或泛解析为什么字段名称。啰嗦一点,再看一下示例
1 | type T1 struct { |
结构体标签对类型转换没有影响
将一个结构体的值,转换为其他类型时,需要结构体底层类型必须相同。但是字段标签不包括在内。
1 | type T1 struct { |
结构体标签的使用场景
(Un)marshaling
在 Golang 里边,结构体标签用的最多的地方,就是编码和解码。上面我们已经举了好几个关于 json 编码和解码的例子,不再多说。json(encoding/json) 只是编码解码的一种格式而已,其实 xml(encoding/xml)也在使用结构体标签。
在HTTP表单数据处理上,比如这个包 gorilla/schema,可以将HTTP的POST表单解析为一个结构体。
ORM
ORM,是对象关系映射的缩写(Object-relation mapping),主要用在数据库操作上。Golang里边有很多类似的工具,比如:gorm 等。
go vet
golang 的编译器对结构体标签的格式并不会强制检查其是否是合法的键值对。但是,go vet 工具会做检查。所以,为了程序的健壮性和正确性,我们可以用 go vet 作为 CI pipeline 的一部分,用在编译之前。
前面我提到,golang的结构体标签的键值对之间用空格分割,如果你一不小心用逗号分隔,还忘了这个事情,后续程序出问题,还真的无法立刻就能排查出来,这个时候,go vet 用处就很大了。
1 | package main |
1 | > go vet tags.go |
其他
结构体标签还有很多其他用处。比如:配置管理、结构体字段的默认值、校验、命令行参数描述等等。golang 官方仓库的wiki中也有一些说明,有兴趣的可以看一下: Well-known-struct-tags