Go语言可通过reflect包递归遍历结构体嵌套字段,仅访问导出字段,累积字段名构建完整路径(如User.Profile.Address.Street),对指针解引用、接口取底层值,跳过不可反射类型,支持基础类型、map、slice等直接取值。

使用反射遍历结构体嵌套字段
Go 语言没有内置的“深度遍历结构体”语法,但可通过 reflect 包递归获取所有导出字段(首字母大写)及其嵌套值。核心思路是:对每个字段检查其类型,若为结构体则递归处理,否则记录字段路径和值。
构建字段路径并提取值
为避免丢失嵌套层级信息,需在递归时累积字段名,形成类似 User.Profile.Address.Street 的完整路径。关键点:
- 只访问导出字段(
CanInterface()为 true 且字段名首字母大写) - 跳过函数、chan、unsafe.Pointer 等不可反射取值的类型
- 对指针字段,先
Elem()解引用再处理;对接口字段,用Interface()获取底层值再反射 - 基础类型(int、string、bool 等)、map、slice 可直接取值,无需深入
示例:打印所有嵌套字段路径与值
以下函数接收任意结构体指针,输出每个可访问字段的完整路径及 Go 字面量形式的值:
func PrintStructFields(v interface{}, prefix string) {
rv := reflect.ValueOf(v)
if rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
if rv.Kind() != reflect.Struct {
return
}
rt := rv.Type()
for i := 0; i < rv.NumField(); i++ {
field := rt.Field(i)
value := rv.Field(i)
// 跳过非导出字段
if !value.CanInterface() {
continue
}
fullPath := prefix + field.Name
if !value.IsValid() {
fmt.Printf("%s = \n", fullPath)
continue
}
// 基础类型或非结构体:直接打印
if value.Kind() != reflect.Struct && value.Kind() != reflect.Ptr {
fmt.Printf("%s = %v\n", fullPath, value.Interface())
continue
}
// 指针:解引用后继续
if value.Kind() == reflect.Ptr && !value.IsNil() {
PrintStructFields(value.Interface(), fullPath+".")
continue
}
// 结构体:递归
if value.Kind() == reflect.Struct {
PrintStructFields(value.Interface(), fullPath+".")
}
}
}
调用 PrintStructFields(&myUser, "") 即可输出带层级的完整字段信息。
立即学习“go语言免费学习笔记(深入)”;
注意事项与限制
反射无法访问私有(小写开头)字段,也无法获取 struct tag 以外的定义元信息。若需支持 JSON 标签映射或忽略空字段,需额外解析 field.Tag.Get("json") 并结合逻辑过滤。另外,循环引用结构体将导致无限递归,生产环境建议加深度限制或已访问地址缓存。









