https://blog.csdn.net/weixin_45193103/article/details/123876319
https://blog.csdn.net/qq_49723651/article/details/122005291
https://juejin.cn/post/7005465902804123679
学一点,整一点,基本都是综合别人的,弄成我能理解的内容
Tag定义
Tag用于标识结构体字段的额外属性,有点类似于注释。标准库reflect包中提供了操作Tag的方法
Tag是结构体在编译阶段关联到成员的元信息字符串,在运行的时候通过反射的机制读取出来。
结构体的字段的定义。在reflect包中,使用结构体structField表示结构体的一个字段
type StructField struct {
Name string
//字段名
Type Type
//字段类型
Tag StructTag //Tag 的类型为structTag,实际上它是一个string类型的别名
//Tag
}
key会指定反射的解析方式,如下: json(JSON标签) 、orm(Beego标签)、gorm(GORM标签)、bson(MongoDB标签)、form(表单标签)、binding(表单验证标签)、yaml
Tag的意义
Go语言的反射特性可以动态地给结构体成员赋值,正是因为有Tag,在赋值前可以使用Tag来决定赋值的动作。
比如,官方的encoding/json包可以将一个JSON 数据“Unmarshal”进一个结构体,此过程中就使用了 Tag。该包定义了一些Tag 规则,只要参考该规则设置tag 就可以将不同的JSON数据转换成结构体。
在Golang中,命名都是推荐用驼峰方式,并且在首字母大小写有特殊的语法含义:包外无法引用。但是由于经常需要和其它的系统进行数据交互,例如转成json格式,存储到mongodb啊等等。这个时候如果用属性名来作为键值可能不一定会符合项目要求。
而通过Tag,我们可以在转换成其它格式的时候,使用其中定义的字段作为键值。
type User struct {
UserId int `json:"user_id"`
UserName string `json:"user_name"`
}
func main() {
u := &User{UserId: 1, UserName: "张三"}
j, _ := json.Marshal(u)
fmt.Println(string(j))
}
//{"user_id":1,"user_name":"张三"}
Tag约定
结构体标签由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。键值对之间使用一个空格分隔,具体的格式如下:
`key1:"value1" key2:"value2" key3:"value3"...` // 键值对用空格分隔
Tag 本身是一个字符串,单从语义上讲,任意的字符串都是合法的。但它有一个约定的格式,那就是字符串由key:"value"
组成。
key 必须是非空字符串,字符串不能包含控制字符、空格、引号、冒号;
value 以双引号标记的字符串。
注意: key和 value之间使用冒号分隔,冒号前后不能有空格,多个key : "value"之间由空格分开。
Kind string `json:"kind, omitempty" protobuf:"bytes,1, opt, name=kind"`
key一般表示用途,比如.json表示用于控制结构体类型与JSON格式数据之间的转换,protobuf表示用于控制序列化和反序列化。value一般表示控制指令,具体控制指令由不同的库指定。
获取Tag值
package main
import (
"fmt"
"reflect"
)
type Food struct {
Apple string `fruit:"apple"`
Tomato string `vegetable:"tomato"`
}
func main() {
t := reflect.TypeOf(Food{})
f, _ := t.FieldByName("Apple")
fmt.Println(f.Tag)
// Tag.Lookup
v, ok := f.Tag.Lookup("fruit")
fmt.Printf("%s, %t\n", v, ok)
// Tag.Get
v = f.Tag.Get("fruit")
fmt.Println(v)
}
运行结果
fruit:"apple"
apple, true
apple
json Tag
type Student struct {
ID int `json:"-"` // 该字段不进行序列化
Name string `json:name,omitempy` // 如果为类型零值或空值,序列化时忽略该字段
Age int `json:age,string` // 重新指定字段类型,支持string、number、boolen
}
https://studygolang.com/static/pkgdoc/pkg/encoding_json.htm
json编码
type User struct {
ID int `json:"id"` // 编码后的字段名为 id
Name string // 编码后的字段名为 自定义成员名 Name
age int // 未导出字段不能编码
}
在 Go 语言中,如果一个结构体的字段名首字母是大写,那么它就是一个 “导出字段”(Exported Field),意味着它可以被外部的包访问和操作。反之,如果一个字段名首字母是小写,那么它就是一个 “未导出字段”(Unexported Field),只能在当前的包内部被访问和操作。
在进行 JSON 编码(encoding)或解码(decoding)时,只有结构体的导出字段才会被处理。
gorm
GORM Tag名大小写不敏感,建议使用camelCase风格,多个标签定义用分号(;)分隔
// 新闻模型
type News struct {
Model
Title string `gorm:"column:title;type:string;not null,default:''"`
Content Content `gorm:"foreignKey:NewsId" json:"content"` //指定外键
}
// AUTO_INCREMENT 不生效
Id uint64 `gorm:"column:id;primaryKey;type:bigint(20);autoIncrement;comment:'主键'"`
// AUTO_INCREMENT 不生效
Id uint64 `gorm:"column:id;type:bigint(20);autoIncrement;comment:'主键'"`
// AUTO_INCREMENT 生效 gorm会自动根据字段类型设置数据库字段类型并设置为主键
Id uint64 `gorm:"column:id;autoIncrement;comment:'主键'"` //写成AUTO_INCREMENT也可以
yaml inline
在 Go 语言结构体标签(struct tags)中,yaml:",inline"
是一种特殊的标识符,表示“内联”,也就是嵌入。
这个标识符的作用主要有两层含义:
- 对于 Go 语言的结构体字段(Struct Fields)本身,如果这个字段的类型是另一个结构体,那么在编组和解组这个结构体的时候,它的字段会被当作是外层结构体的字段来处理。
- 对于解析 YAML 文件时,如果一个字段被标记为 inline,那么在这个 YAML 文件被解析成 Go 语言结构体的时候,这个字段将会把它的子节点看作是自己的直接字段。
type Person struct {
Name string `yaml:"name"`
Age int `yaml:"age"`
}
type Employee struct {
Person `yaml:",inline"`
Job string `yaml:"job"`
}
当你在解析如下的 YAML 时:
yaml
name: Mike
age: 30
job: engineer
那么,这个 YAML 将会被解析成如下的 Go 语言结构体:
Employee{
Person: Person{
Name: "Mike",
Age: 30,
},
Job: "engineer",
}
可以看到,虽然 YAML 文件中并没有 Person 这一层,但是由于 Person 被标记为 inline,Name 和 Age 字段就像是 Employee 的直接字段一样被处理了。
Go vet
vet 是 golang 中自带的静态分析工具,可以让我们检查出 package 或者源码文件中一些隐含的错误
Go 编译器没有强制执行传统的 struct 标签格式,但是 vet 就是这样做的,所以值得使用它,例如作为 CI 管道的一部分。
type T struct {
f string "one two three"
}
func main() {}
> Go vet tags.go
tags.go:4: struct field tag `one two three` not compatible with reflect.StructTag.Get: bad syntax for struct tag pair
//在 Go 语言中,当你使用结构体标签(struct tag)去描述一个结构体中的字段时,其格式必须严格遵守 "键:值"("key:value")的形式。
type T struct {
Name string "json:Name"
}
$ go vet
# command-line-arguments
./main.go:8: struct field tag "json:Name" not compatible with reflect.StructTag.Get: bad syntax for struct tag value
//JSON 标签的字段名 Name 是以大写字符开始的,这不符合 Go 语言的标准