Go struct 标签

Go 结构体字段的元数据标签,通过反射读取,广泛用于 JSON 序列化、数据库映射和参数校验等场景。

#type / concept #status / growing #tech / dev #resource / go

[!info] related notes

Go struct 标签

一句话定义

struct tag 是附加在结构体字段上的元数据字符串,由 reflect 包在运行时读取,驱动 JSON 编解码、ORM 映射、参数校验等框架行为。

核心机制 / 工作原理

  1. 语法格式:标签是反引号包裹的原始字符串,写在字段类型之后。格式为一个或多个 key:"value" 对,空格分隔:

    Name string `json:"name" db:"user_name" validate:"required"`
  2. 通过反射读取:使用 reflect.Type.Field(i).Tag.Get("key") 获取指定 key 的值。Lookup 方法同时返回值和是否存在。

  3. 常用标签体系

    Key来源作用
    jsonencoding/json控制 JSON 序列化字段名和行为
    dbdatabase/sql, sqlx数据库列名映射
    yamlgopkg.in/yaml.v3YAML 序列化
    validatego-playground/validator参数校验规则
    gormGORM ORM数据库模型定义
    mapstructuremapstructuremap 到 struct 的映射
  4. json 标签详解

    • json:"name" — 指定 JSON 键名为 name
    • json:"-" — 该字段不参与 JSON 编解码
    • json:",omitempty" — 零值时省略
    • json:"name,omitempty" — 组合使用
    • json:",string" — 将数值编码为 JSON 字符串
  5. 标签是编译后不变的常量:标签值在编译时确定,嵌入到二进制中,运行时通过反射访问。不能使用变量或动态生成。

最小例子 / 最小场景

package main

import (
	"encoding/json"
	"fmt"
	"reflect"
)

type User struct {
	ID        int    `json:"id" db:"id"`
	Name      string `json:"name" db:"user_name" validate:"required"`
	Email     string `json:"email,omitempty" db:"email"`
	Password  string `json:"-" db:"password"`       // JSON 中不暴露
	Age       int    `json:"age,string" db:"age"`    // JSON 中编码为字符串
	createdAt string  // 未导出字段, 反射无法读到 tag
}

func main() {
	u := User{ID: 1, Name: "Alice", Password: "secret", Age: 30}

	// JSON 序列化
	data, _ := json.Marshal(u)
	fmt.Println(string(data))
	// {"id":1,"name":"Alice","age":"30"}  — Password 被跳过, Email 因 omitempty 省略, Age 变成字符串

	// 反射读取 tag
	t := reflect.TypeOf(User{})
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		jsonTag, _ := field.Tag.Lookup("json")
		dbTag, _ := field.Tag.Lookup("db")
		fmt.Printf("%s -> json=%q, db=%q\n", field.Name, jsonTag, dbTag)
	}
	// ID -> json="id", db="id"
	// Name -> json="name", db="user_name"
	// ...
}

为什么重要

  • 框架驱动的核心机制:Go 生态中的 web 框架、ORM、配置解析、参数校验全部依赖 struct tag,不理解 tag 就无法有效使用这些工具。
  • 数据契约的声明式表达:通过 tag 在一个地方声明 JSON 键名、数据库列名、校验规则,比在每个业务函数中手动处理更清晰、更不容易出错。
  • API 文档生成:Swagger/OpenAPI 工具链(如 swaggo/swag)通过读取 struct tag 和注释自动生成接口文档。

边界与易混淆点

  • tag 只能是字符串常量:不能用 const 变量作为 tag 值(json:MyConst 是非法的),也不支持字符串插值。
  • 未导出字段的 tag 不可见reflect 无法读取未导出字段的 tag,因此 createdAt string \db:“created_at”“ 中的 tag 会被忽略。
  • 多个框架共存时注意冲突:同一个字段上多个 tag 用空格分隔(不是逗号),如 json:"name" db:"name"。如果某个框架的 tag 值本身就含空格,需要确认该框架的解析规则。
  • omitempty 对零值的理解:数值 0、空字符串 ""falsenil 指针、空 slice/map 都是零值,都会被 omitempty 省略。如果 0 是有效值,需要考虑用指针 *int 或自定义类型。
  • 反射读取 tag 有性能开销:热路径中应缓存 tag 解析结果,而非每次调用都通过反射读取。
创建于 2026/6/25 更新于 2026/6/25