← 嵌套与嵌入 | 接口 →

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