结构体标签( Struct Tags ) 是贴在字段上的“便利贴”。
- 本质 :它是一段字符串元数据
- 位置 :紧跟在结构体字段声明的后面,用反引号
` `包裹 - 作用 :它本身不参与程序的逻辑运算,也不会影响字段的取值和赋值。它静静地呆在那里,等待特定的“工具”去读取它
type User struct {
Username string `json:"user_name" mapstructure:"user_name"`
Password string `json:"-"`
}
这些并非语言标准,是“非入侵式”:
- 编译器不关心 : Go 的编译器在编译代码时,会忽略这些标签。如果写错(比如拼写错误),编译器通常 不会报错,程序能正常运行,只是标签不起作用而已
- 库的约定 :这些标签是给第三方库或标准库中特定包(如
encoding/json)看(用)的json:"..."是给encoding/json包看的mapstructure:"..."是给mapstructure库看的gorm:"..."是给 GORM 数据库框架看的
Go 语言本身不定义标签的“含义”,含义是有读取它的代码(库)决定的。
| 标签键 | 用途 | 示例 |
|---|---|---|
json | 控制 JSON 序列化/反序列化 | json:"name,opitempty" (为空时忽略) |
mapstructure | Viper 等库用它来映射配置 | mapstructure:"db_host" |
gorm | GORM 框架用它映射数据库表 | gorm:"type:varchar(100)" |
validate | 验证库用它做参数校验 | validate:"required,min=3" |
xml | 处理 XML 数据 | xml:"item" |
工作机制 -- 反射
- 定义 :在
User结构体上定义了json:"user_name" - 调用 :调用
json.Marshal(user) - 发射 :
json包在内部通过反射查看User结构体 - 读取 :发现
json标签,读取到值user_name - 执行 :在生成 JSON 字符串时,把字段写成
user_name,而不是Username
package main
import (
"fmt"
"reflect"
)
// 1. 定义一个带标签的结构体
type User struct {
Username string `json:"user_name" validate:"required"`
Password string `json:"-"`
}
func main() {
// 2. 使用 reflect.TypeFor 获取类型信息,这就是反射的“眼睛”
t := reflect.TypeFor[User]()
// 3. 遍历结构体的所有字段
for filed := range t.Fields {
// 4. 读取字段上的标签
jsonTag := field.Tag.Get("json")
validateTag := field.Tag.Get("validate")
fmt.Printf("字段名: %s\n", field.Name)
fmt.Printf(" -> json 标签内容: %s\n", jsonTag)
fmt.Printf(" -> validate 标签内容: %s\n", validateTag)
}
}
运行得到:
字段名: Username
-> json 标签内容: user_name
-> validate 标签内容: required
字段名: Password
-> json 标签内容: -
-> validate 标签内容:
常用到结构体标签的
Gin/Echo/Fiber(Web 框架)
- 标签名 :
uri,form, - 用途 :使用结构体标签用于 URL 路径参数、查询字符串或表单数据绑定到结构体字段。
type Login struct {
// 绑定表单字段并校验必填
User string `form:"user" binding:"required"`
Password string `from:"password" binding:"required"`
}
go-playground/validator(参数校验)
- 标签名 :
validate - 用途 : 这是 Go 中最流行的校验库(Gin 内部也使用它)。它允许在标签内部写复杂的校验规则,比如正则、长度校验、跨字段比较
type User struct {
// 必填且必须填写邮箱格式
Email string `validate:"required,email"`
// 大于等于 18
Age int `validate:"gte=10"`
}
sqlx ( SQL 数据库)
- 标签名 :
db - 用 途 :它是标准库
database/sql的扩展。当从数据库查询数据到结构体时,它根据db标签来映射列名。很多不使用完整 ORM (如 GORM )但想要便利映射的开发者喜欢使用它
type User struct {
ID int `db:"id"`
Name string `db:"name"`
// 数据库列名可能是下划线风格
Email string `db:"email_address"`
}
MongoDB Driver ( NoSQL 数据库)
- 标签名 :
bson - 用途 :类似于 JSON ,用于 MongoDB 文档与 Go 结构体之间的映射
type User struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Name string `bson:"name"`
}
Redis ( go-redis )
- 标签名 :
redis - 用途 :用于将 Redis 的 Hash 结构直接扫描到 Go 结构体中
type User struct {
Name string `redis:"name"`
Age int `redis:"age"`
}
mapstructure (通用解码库)
- 标签名 :
mapstructure - 用途 : Viper 就使用该库。它非常强大,用于将
map[string]interface{}(比如从 API 返回的元素数据或配置文件)转换为结构体
type Config struct {
Port int `mapstructure:"server_port"`
}
copier (字段拷贝库)
- 标签名 :
copy - 用途 :用于两个不同的结构体之间快速拷贝字段(比如从
UserModel拷贝到UserResponse)
type UserResponse struct {
// 从源结构体 的 username 字段拷贝过来
Name string `copy:"username"`
}
spf13/cobra (命令行工具框架)
- 标签名 :
mapstructure(配合 Viper ) 或自定义标签 - 用途 :虽然 Cobra 主药出炉命令,但配合 Viper 时,常用语将命令行参数( Flags )绑定到配置结构体
env (环境变量加载库)
- 标签名 :
env - 用途 :直接将环境变量注入到结构体中,非常适合 12-Factor APP 开发模式
type Config struct {
Port int `env:"PORT"`
// 支持默认值
BDHost string `env:"DB_HOST" envDefault:"localhost"`
}
protobuf ( Google 远程过程调用协议)
- 标签名 :
protobuf - 用途 :如果使用 gRPC ,生成 Go 代码中包含大量的 protobuf 标签,用于定义字段类型、序号等
type UserRequest struct {
ID int32 `Protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"`
}
toml (配置文件解析)
- 标签名 :
toml - 用途 :用户解析 TOML 格式的配置文件( Rust 社区很喜欢用, Go 里也有应用 )
type Config struct {
Title string `toml:"title"`
}