Struct Tag - 结构体标签
Struct Tag 是结构体字段的元数据,用于提供额外信息。Tag 被反射读取,广泛应用于 JSON 序列化、数据库映射、参数校验等场景。
Tag 基础
📝 Tag 语法
package main
import "fmt"
import "reflect"
type User struct {
ID int `json:"id" db:"user_id" validate:"required"`
Username string `json:"username" db:"username" validate:"min=3"`
Email string `json:"email" db:"email" validate:"email"`
Password string `json:"-" db:"password"`
}
func main() {
var u User
t := reflect.TypeOf(u)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
tag := field.Tag
fmt.Printf("Field: %s\n", field.Name)
fmt.Printf(" JSON: %s\n", tag.Get("json"))
fmt.Printf(" DB: %s\n", tag.Get("db"))
fmt.Printf(" Validate: %s\n", tag.Get("validate"))
}
}
// Tag 格式:
// `key:"value" key2:"value2"`
// 多个 tag 用空格分隔
// 值用反引号包裹
JSON Tag
📝 JSON 序列化标签
package main
import (
"encoding/json"
"fmt"
)
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price"`
Stock int `json:"stock,omitempty"`
Internal string `json:"-"`
Secret string `json:"-,omitempty"`
}
func main() {
p := Product{
ID: 1,
Name: "Laptop",
Price: 999.99,
Stock: 0,
Internal: "internal data",
Secret: "secret",
}
data, _ := json.MarshalIndent(p, "", " ")
fmt.Println(string(data))
// 输出:
// {
// "id": 1,
// "name": "Laptop",
// "price": 999.99
// }
// stock 为 0 被 omitempty 省略
// Internal 被 "-" 忽略
// Secret 被 "-" 忽略
}
💡 JSON Tag 选项
- json:"name": 自定义字段名
- json:"-": 忽略该字段
- json:",omitempty": 零值时省略
- json:",string": 数字转为字符串
校验 Tag
📝 Validator 标签
type RegisterRequest struct {
Username string `validate:"required,min=3,max=20"`
Email string `validate:"required,email"`
Password string `validate:"required,min=8"`
Age int `validate:"min=18,max=100"`
Role string `validate:"oneof=admin user"`
}
// 常用校验规则:
// required: 必填
// min/max: 最小/最大值
// email: 邮箱格式
// oneof: 枚举值
// len: 固定长度
// regexp: 正则匹配
自定义 Tag 解析
📝 解析 Tag 选项
package main
import (
"fmt"
"reflect"
"strings"
)
type Config struct {
Host string `env:"DB_HOST" default:"localhost"`
Port int `env:"DB_PORT" default:"5432"`
}
func parseTag(tag string) (string, map[string]string) {
parts := strings.Split(tag, ",")
name := parts[0]
opts := make(map[string]string)
for _, part := range parts[1:] {
kv := strings.SplitN(part, "=", 2)
if len(kv) == 2 {
opts[kv[0]] = kv[1]
} else {
opts[kv[0]] = ""
}
}
return name, opts
}
func main() {
var cfg Config
t := reflect.TypeOf(cfg)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
tag := field.Tag.Get("env")
name, opts := parseTag(tag)
fmt.Printf("Field: %s, Env: %s, Default: %s\n",
field.Name, name, opts["default"])
}
}
最佳实践
✅ Tag 使用建议
- 一致性: 同一项目使用统一的 tag 命名
- 简洁: tag 值尽量简短
- 文档: 自定义 tag 要有文档说明
- 验证: 重要字段添加 validate tag