用Go反射遍历任意结构体字段需传指针,通过reflect.ValueOf和reflect.TypeOf获取值与类型,递归处理结构体、切片、map等,支持标签解析与缩进打印。

用 Go 语言遍历任意结构体字段并打印,核心是 反射(reflect)。Go 不支持传统意义上的“运行时字段枚举”,但 reflect 包提供了安全、通用的方案,适用于任意嵌套结构体、指针、基础类型、切片、map 等。
获取结构体反射值并遍历字段
必须传入结构体指针(*T),否则 reflect.Value 无法获取导出字段(首字母大写)。使用 reflect.TypeOf 和 reflect.ValueOf 获取类型和值信息,再通过 .NumField() 和 .Field(i) 遍历:
- 先判断是否为指针,用
.Elem()解引用到实际结构体值 - 确保值是结构体(
.Kind() == reflect.Struct) - 用
.Type().Field(i)获取字段名、标签;.Field(i)获取字段值
处理嵌套结构体与常见类型
递归是关键。对每个字段值做类型判断,再决定是否深入:
- 结构体或指针指向结构体:递归调用打印函数(注意空指针检查)
- 切片/数组:遍历元素,对每个元素递归处理
- map:遍历 key-value,value 递归处理
- 基本类型、字符串、布尔等:直接格式化输出
- nil 接口、nil 指针、未导出字段:跳过或打日志提示(不 panic)
支持结构体标签(tag)与缩进美化
通过 structField.Tag.Get("json") 或自定义 tag(如 "print")控制显示逻辑。配合层级深度参数实现缩进,提升可读性:
立即学习“go语言免费学习笔记(深入)”;
- 每层递归传入当前缩进级别(如
indent + " ") - 字段名优先使用 tag 中的
json名(若存在且非 "-"), fallback 到原始字段名 - 对 slice/map 长度、结构体地址等元信息可选择性打印(增强调试能力)
完整可用示例代码
以下是一个轻量、健壮的打印函数(已处理空指针、循环引用简化、非导出字段跳过):
func PrintStruct(v interface{}, indent string) {
rv := reflect.ValueOf(v)
if !rv.IsValid() {
fmt.Println(indent + "(invalid)")
return
}
if rv.Kind() == reflect.Ptr {
if rv.IsNil() {
fmt.Println(indent + "")
return
}
fmt.Println(indent + "&{")
PrintStruct(rv.Elem().Interface(), indent+" ")
fmt.Println(indent + "}")
return
}
if rv.Kind() != reflect.Struct {
fmt.Printf("%s%v\n", indent, rv.Interface())
return
}
rt := reflect.TypeOf(v).Elem()
for i := 0; i < rv.NumField(); i++ {
field := rt.Field(i)
value := rv.Field(i)
if !value.CanInterface() { // 非导出字段跳过
continue
}
name := field.Name
if jsonTag := field.Tag.Get("json"); jsonTag != "" && jsonTag != "-" {
if idx := strings.Index(jsonTag, ","); idx > 0 {
name = jsonTag[:idx]
} else {
name = jsonTag
}
}
fmt.Printf("%s%s: ", indent, name)
PrintStruct(value.Interface(), indent+" ")
}
}











