结构体标签须用反引号包裹,键值对以空格分隔,键后跟冒号及双引号包围的值;reflect.structtag.get() 返回首个匹配键的原始值,不校验语法,空返回不代表标签不存在。

结构体标签怎么写才被 reflect 正确识别
Go 的结构体标签(struct tag)本质是字符串,只有按特定格式书写,reflect.StructTag 才能解析。必须用反引号包裹,键值对用空格分隔,键名后跟冒号和双引号包围的值——任何偏差都会导致 reflect.StructField.Tag.Get() 返回空字符串或 panic。
- ✅ 正确:
`json:"name" db:"user_name" validate:"required"` - ❌ 错误:
`json:name`(缺双引号)、`json:"name" db:user_name`(值未引号)、"json:\"name\""(用双引号包裹整个 tag,无法被 parser 识别) - 标签键名不区分大小写,但惯例全小写;重复键名时,后出现的覆盖前一个
用 reflect.StructTag.Get() 提取单个标签值
这是最常用场景:从字段上读取 json、db 或自定义标签内容。注意 Get() 只返回第一个匹配键的值,且不校验语法——哪怕标签写成 `json:"name,omit"`,它也照常返回 "name,omit",后续解析得你来负责。
type User struct {
Name string `json:"name" db:"user_name"`
}
field, _ := reflect.TypeOf(User{}).FieldByName("Name")
jsonTag := field.Tag.Get("json") // → "name"
dbTag := field.Tag.Get("db") // → "user_name"
- 如果字段没该标签,
Get()返回空字符串,不是 nil - 不要对
Get()结果做== ""判断来“确认标签存在”——空值可能是有意设置(如json:"-") - 若需解析带选项的标签(如
json:"name,omitempty"),得手动用strings.Split()或正则拆解
反射遍历结构体字段并批量处理标签
实际项目中常需统一处理所有字段的某个标签,比如生成 SQL 插入语句、校验必填字段、构建 API 响应映射。这时要遍历 reflect.Type 的每个 Field,逐个提取并判断。
func GetDBColumns(v interface{}) []string {
t := reflect.TypeOf(v)
if t.Kind() == reflect.Ptr {
t = t.Elem()
}
var cols []string
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
if db := f.Tag.Get("db"); db != "" && db != "-" {
cols = append(cols, db)
}
}
return cols
}
- 务必检查指针类型:传入
&User{}时,reflect.TypeOf得先.Elem()才能得到结构体本身 - 显式忽略
"-"是惯用做法(模仿json标签语义),否则db:"-"也会被当作有效列名 - 字段是否导出(首字母大写)直接影响
reflect能否访问——未导出字段在反射中不可见,标签再全也没用
自定义标签解析器要注意转义与空格边界
标准库只认 json、xml 等少数标签,其余全靠自己解析。常见坑是没处理好逗号分隔的选项、引号嵌套、或空格截断。例如 `validate:"min=10,max=100"` 中的等号前后有空格,直接 strings.Fields() 会切出 "min=10,max=100",而非两个独立选项。
立即学习“go语言免费学习笔记(深入)”;
- 推荐用
structtag第三方包(如github.com/mitchellh/reflectutil或更轻量的go-taglib),它已处理转义、引号、逗号分割等边界情况 - 手写解析时,别用
strings.Split(tag, ","),改用structtag.Parse()或至少用strconv.Unquote()解包值后再拆 - 标签值里不能出现未转义的双引号,否则
reflect.StructTag解析直接失败,整个 tag 被视为无效










