Go中遍历结构体字段需用reflect包,仅支持导出字段:用reflect.TypeOf获取类型,NumField()和Field(i)遍历,field.Name、field.Type、field.Tag分别获取字段名、类型、标签;非导出字段被忽略。

在 Go 中遍历结构体字段并动态获取字段名和类型,需要借助 reflect 包。Go 的结构体本身不支持原生的运行时字段枚举,但通过反射可以安全、可控地读取公开字段(首字母大写的导出字段)的名称、类型、标签等信息。
使用 reflect.TypeOf 获取结构体类型信息
先用 reflect.TypeOf 获取结构体的类型对象,再调用 .NumField() 和 .Field(i) 遍历每个字段:
-
field.Name:字段名(字符串,如"Name") -
field.Type:字段的类型(reflect.Type),可进一步调用.Name()或.Kind() -
field.Tag:结构体标签(如`json:"name"`),可用Get("json")提取
注意:只能访问**导出字段**(首字母大写),未导出字段会被忽略且无法通过反射读取值(即使能拿到字段描述,field.Type 可访问,但 reflect.Value.Field(i) 会 panic)。
完整示例:打印字段名、类型和 JSON 标签
以下是一个典型用法:
立即学习“go语言免费学习笔记(深入)”;
package mainimport ( "fmt" "reflect" )
type User struct { Name string
json:"name"Age intjson:"age"Email stringjson:"email,omitempty"id int // 小写,非导出字段,不会出现在遍历中 }func printStructFields(v interface{}) { t := reflect.TypeOf(v) if t.Kind() == reflect.Ptr { t = t.Elem() } if t.Kind() != reflect.Struct { fmt.Println("不是结构体类型") return }
fmt.Printf("结构体 %s 字段列表:\n", t.Name()) for i := 0; i < t.NumField(); i++ { field := t.Field(i) typeName := field.Type.Name() if typeName == "" && field.Type.Kind() == reflect.Ptr { typeName = "*" + field.Type.Elem().Name() } jsonTag := field.Tag.Get("json") fmt.Printf("- %s: %s (JSON: %q)\n", field.Name, field.Type, jsonTag) }}
func main() { u := User{Name: "Alice", Age: 30} printStructFields(u) // 传值或传指针均可(函数内已处理) }
输出类似:
结构体 User 字段列表: - Name: string (JSON: "name") - Age: int (JSON: "age") - Email: string (JSON: "email,omitempty")获取字段值需配合 reflect.Value
仅获取字段名和类型用
reflect.TypeOf即可;若还需读取当前值,需用reflect.ValueOf,并确保值是可寻址的(如传指针或变量):
-
v := reflect.ValueOf(&u).Elem()得到可读写的结构体值 -
v.Field(i).Interface()获取第 i 个字段的实际值(注意类型断言或 panic 风险) - 对不可导出字段,
v.Field(i).CanInterface()返回 false,不能安全取值
实用建议与注意事项
- 反射有性能开销,避免在高频路径(如 HTTP 中间件、循环内部)频繁使用
- 字段顺序按源码声明顺序,不是字母序;嵌套结构体需递归处理
field.Type.Kind() == reflect.Struct - 匿名字段(内嵌结构体)也会被列出,
field.Anonymous为 true,可据此做特殊处理 - 生成 JSON Schema、自动表单渲染、ORM 映射等场景适合用此方式统一处理结构体元信息










