Go反射获取结构体字段名需先确认类型为结构体,用reflect.TypeOf获取类型信息后遍历Field;指针需Elem()解引用,非导出字段Name为空且PkgPath非空。
用 reflect.TypeOf 和 Field 获取结构体字段名
Go 的反射不能直接从任意值“提取字段名”,必须先确认它是结构体类型,再通过 reflect.TypeOf 拿到类型信息,再遍历其 Field。如果传入的是指针,得先用 Type.Elem() 解引用;如果传的是非导出字段(小写开头),反射会返回空字符串而非报错——这点极易被忽略。
实操建议:
- 始终用
reflect.ValueOf(v).Elem()处理指针结构体,避免panic: reflect: call of reflect.Value.Type on zero Value - 字段名通过
field.Name获取,但仅对导出字段有效;非导出字段的Name为空,Tag也读不到 - 用
field.PkgPath != ""可判断是否为非导出字段(返回非空字符串即未导出)
type User struct {
Name string `json:"name"`
age int `json:"age"` // 小写 → 非导出
}
v := reflect.ValueOf(&User{}).Elem()
for i := 0; i < v.NumField(); i++ {
field := v.Type().Field(i)
fmt.Println(field.Name) // 输出 "Name",但不会输出 "age"
}
带结构体标签(如 json)的字段名映射
很多场景下你真正想要的不是 Go 字段名,而是序列化用的键名(比如 json 标签值)。这时不能只看 field.Name,得解析 field.Tag.Get("json"),并注意处理逗号后缀(如 json:"name,omitempty")。
实操建议:
-
field.Tag.Get("json")返回完整 tag 值,需手动截断逗号后内容;标准库有strings.SplitN(tag, ",", 2)提取主键名 - 若 tag 为
-(如json:"-"),表示该字段应被忽略,此时不应参与字段名映射 - 空 tag(
json:"")或未声明 tag 时,Get返回空字符串,此时应回退到field.Name
为什么 reflect.Value.FieldByName 不能反向获取字段名
FieldByName 是按名字查字段,不是从字段反推名字——它接收字符串参数,返回 reflect.Value,没有“由 reflect.Value 得到原始字段名”的内置方法。这是因为反射对象在运行时已脱离源码符号表,字段名只是结构体类型元数据的一部分,只能通过 Type.Field(i) 顺序遍历获得。