Go 中 struct tag 是编译期保留的字符串,非语法特性,需用 reflect.StructTag.Get 安全提取;必须反引号包裹、字段导出,值内双引号需转义;运行时不可修改,解析自定义 tag 须手动拆分且注意冒号转义。

Go 里没有“注解”或“属性”,但 reflect.StructTag 就是你能拿到的最接近的东西——它不是魔法,只是个字符串解析器;能不能用好,取决于你是否理解它的边界和限制。
struct tag 本质是字符串,不是语法特性
结构体字段后面的反引号内容(如 `json:"id" filtered:"true"`)在编译期被原样保留为 string,Go 编译器不解析、不校验、不执行。它只参与类型唯一性判定:两个仅 tag 不同的 struct 是不同类型。
- 必须用反引号
`包裹,用双引号或单引号会报错invalid character literal - 字段必须导出(首字母大写),否则
reflect.StructField.Tag返回空字符串 - 标签值内部的双引号需转义:
`json:"name,omitempty"`合法,`json:"name,omitemtpy"`(少一个引号)直接编译失败
用 reflect.StructTag.Get 安全取值
Tag.Get("key") 是标准、安全的提取方式,它隐含处理了不存在 key 的情况——返回空字符串,而不是 panic 或 error。
- 对不存在的键(如
field.Tag.Get("unknown"))返回"",别用== nil判断 -
json:"-"这种值会被原样返回,需业务层自行识别(比如 encoding/json 就把它当作“忽略该字段”) - 不要手动
strings.Split整个 tag 字符串——reflect.StructTag内部已按空格分隔并解析键值对,重复解析易出错
解析带参数的自定义 tag(如 gorm:"column:user_name;type:varchar(100)")
Tag.Get("gorm") 只返回 "column:user_name;type:varchar(100)" 这一整段字符串,后续拆解得你自己来。
立即学习“go语言免费学习笔记(深入)”;
- 推荐用
strings.Split按";"分割,再对每个子项用strings.SplitN(..., ":", 2)提取 key/value - 注意 value 中可能含冒号(如
default:"now()"),所以必须用SplitN(..., ":", 2)而非Split - 别假设顺序:
gorm标签不保证column一定在type前面,应统一用 map 存储
为什么不能在运行时修改 struct tag
因为 tag 是类型定义的一部分,而 Go 的类型系统在运行时是只读的。你无法通过反射“改写”某个字段的 tag 字符串。
- 试图赋值给
field.Tag会编译失败:cannot assign to field.Tag - 如果需要动态行为,要么预定义足够多的 tag 组合,要么把逻辑移到字段值上(如用函数替代 tag 控制)
- 常见误操作:想用 tag 控制日志级别,结果发现改不了——这时候该换用配置对象或中间件参数,而不是硬塞进 tag
真正难的从来不是怎么读 tag,而是决定哪些信息该放进去、哪些该交给外部配置或参数传递;tag 太重,代码就失去灵活性;tag 太轻,又得反复写样板逻辑。这个平衡点,得在具体场景里试出来。


















