跳到主要内容

结构体标签( 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" (为空时忽略)
mapstructureViper 等库用它来映射配置mapstructure:"db_host"
gormGORM 框架用它映射数据库表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"`
}