返回

Go 语言系列39:结构体里的 Tag 标签

发布时间:2023-09-06 03:11:30 206

在之前 ​​Go 语言系列14:结构体​​ 我们讲过结构体的使用,一般情况下,我们定义结构体每个字段都是由字段名字以及字段的类型构成,例如:

type Person struct {
Name string
Age int
Gender string
}

Go 语言系列39:结构体里的 Tag 标签_键值对

Tag 的使用

Go 语言系列39:结构体里的 Tag 标签_字段_02


但这一期要讲的是在字段上增加一个属性,这个属性是用反引号括起来的一个字符串,我们称之为 Tag(标签) 。例如:

type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Gender string `json:"gender,omitempty"`
}

结构体的 ​​Tag​​​ 可以是任意的字符串面值,但是通常是一系列用空格分隔的 ​​key:"value"​​​ 键值对序列;因为值中含有双引号字符,因此成员 ​​Tag​​​ 一般用原生字符串面值的形式书写。一般我们常用在 ​​JSON​​ 的数据处理方面。

​json​​ 开头键名对应的值用于控制 encoding/json 包的编码和解码的行为,并且 encoding/... 下面其它的包也遵循这个约定。​​Tag​​​ 中 ​​json​​​ 对应值的第一部分用于指定 ​​JSON​​​ 对象的名字,比如将 Go 语言中的 ​​TotalCount​​​ 成员对应到 ​​JSON​​​ 中的 ​​total_count​​ 对象。

上面的例子中 ​​gender​​​ 字段的 ​​Tag​​​ 还带了一个额外的 ​​omitempty​​​ 选项,表示当 Go 语言结构体成员为空或零值时不生成该 ​​JSON​​​ 对象(这里 ​​false​​ 为零值)。例如:

package main

import (
"encoding/json"
"fmt"
)

type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Gender string `json:"gender,omitempty"`
}

func main() {
// Person 1 without Gender
person1 := Person{
Name: "菜籽",
Age: 25,
}
// 结构体转为 JSON
data1, err := json.Marshal(person1)
if err != nil {
panic(err)
}
// person1 won't print Gender attribute
fmt.Printf("%s\n", data1)

// Person 2 have Gender attribute
person2 := Person{
Name: "Tom",
Age: 18,
Gender: "male",
}
// 结构体转为 JSON
data2, err := json.Marshal(person2)
if err != nil {
panic(err)
}
// person2 will print Gender attribute
fmt.Printf("%s\n", data2)
}

运行该程序输出如下的结果:

{"name":"菜籽","age":25}
{"name":"Tom","age":18,"gender":"male"}

可以看到,因为 ​​Gender​​​ 字段里有 ​​omitempty​​ 属性,因此 encoding/json 在将此结构体对象转化为 ​​JSON​​​ 字符串时,发现对象里面的 ​​Gender​​ 为 false , 0 ,空指针,空接口,空数组,空切片,空映射,空字符串中的一种,就会被忽略。


Go 语言系列39:结构体里的 Tag 标签_键值对

Tag 的获取

Go 语言系列39:结构体里的 Tag 标签_字段_02


Tag 的格式上面已经说了,它是由反引号括起来的一系列用空格分隔的 ​​key:"value"​​ 键值对序列:

`key1:"value1" key2:"value2" key3:"value3"`

那么我们如何获取到结构体中的 Tag 呢?这里我们用反射的方法。

使用反射的方法获取 Tag 步骤如下:

  1. 获取字段
  2. 获取 Tag
  3. 获取键值对

其中获取字段有三种方式,而获取键值对有两种方式。

// 三种获取 field
field := reflect.TypeOf(obj).FieldByName("Name")
field := reflect.ValueOf(obj).Type().Field(i) // i 表示第几个字段
field := reflect.ValueOf(&obj).Elem().Type().Field(i) // i 表示第几个字段

// 获取 Tag
tag := field.Tag

// 获取键值对
labelValue := tag.Get("label")
labelValue,ok := tag.Lookup("label")
// Get 当没有获取到对应 Tag 的内容,会返回空字符串

下面是一个获取 Tag 以及键值对的例子:

package main

import (
"fmt"
"reflect"
)

type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Gender string `json:"gender,omitempty"`
}

func main() {
p := reflect.TypeOf(Person{})
name, _ := p.FieldByName("Name")
tag := name.Tag
fmt.Println("Name Tag :", tag)
keyValue, _ := tag.Lookup("json")
fmt.Println("key: json, value:", keyValue)
}

运行上面的程序输出如下:

Name Tag : json:"name"
key: json, value: name

参考文献:

[1] Alan A. A. Donovan; Brian W. Kernighan, Go 程序设计语言, Translated by 李道兵, 高博, 庞向才, 金鑫鑫 and 林齐斌, 机械工业出版社, 2017.




特别声明:以上内容(图片及文字)均为互联网收集或者用户上传发布,本站仅提供信息存储服务!如有侵权或有涉及法律问题请联系我们。
举报
评论区(0)
按点赞数排序
用户头像
精选文章
thumb 中国研究员首次曝光美国国安局顶级后门—“方程式组织”
thumb 俄乌线上战争,网络攻击弥漫着数字硝烟
thumb 从网络安全角度了解俄罗斯入侵乌克兰的相关事件时间线
下一篇
Go 语言系列43:CGO 编程入门(四) 2023-09-06 00:12:21