
本文介绍如何使用反射(reflect)动态获取结构体中非空字段的名称,跳过零值(如空字符串、nil 指针、零整数等),适用于表单处理、api 请求过滤等场景。
在 Go 中,结构体字段默认初始化为对应类型的零值(如 string 为 "",int 为 0,指针为 nil)。当需要仅处理用户显式赋值的字段(例如解析 Web 表单或配置输入)时,直接遍历所有字段会包含大量无意义的零值字段。此时,需结合 reflect 包判断每个字段是否“非空”。
核心思路是:对每个字段的 reflect.Value 调用自定义的 empty() 判断函数,仅输出非空字段名。
以下是完整可运行的示例代码:
package main
import (
"fmt"
"reflect"
)
type Users struct {
Name string
Password string
Age int
Token *string
}
// empty 判断 reflect.Value 是否为该类型的零值
func empty(v reflect.Value) bool {
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return v.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return v.Uint() == 0
case reflect.String:
return v.String() == ""
case reflect.Ptr, reflect.Slice, reflect.Map, reflect.Interface, reflect.Chan:
return v.IsNil()
case reflect.Bool:
return !v.Bool()
case reflect.Float32, reflect.Float64:
return v.Float() == 0
case reflect.Struct:
// 可选:递归判断结构体是否全为零值(按需扩展)
// 此处简化处理:视为非空(因 Struct 本身不可 nil)
return false
}
return false
}
func main() {
token := "abc123"
u := Users{
Name: "Robert",
Password: "", // 空字符串 → 被排除
Age: 0, // 零值 int → 被排除
Token: &token, // 非 nil 指针 → 保留
}
val := reflect.ValueOf(u)
typ := reflect.TypeOf(u)
fmt.Println("非空字段名:")
for i := 0; i < val.NumField(); i++ {
field := val.Field(i)
if !empty(field) {
fmt.Println(typ.Field(i).Name)
}
}
}输出结果:
非空字段名: Name Token
✅ 关键说明:
- empty() 函数覆盖了常见类型(字符串、数值、布尔、指针、切片、映射等)的零值判断逻辑;
- 对 struct 类型未做深度判空(因其不可为 nil),若需严格检查嵌套结构体是否全为零值,可递归调用 empty();
- 注意:reflect.ValueOf(u) 传入的是值拷贝,因此无法检测指针字段本身的 nil 状态以外的深层内容(如 *string 指向空字符串仍算“非空”,但其解引用值为空——这取决于业务语义);
- 若结构体含导出字段(首字母大写)且需支持 JSON 标签映射等,可进一步结合 StructField.Tag 提取自定义标识(如 json:"name,omitempty")。
此方法轻量、通用,适合构建灵活的数据校验、序列化过滤或动态 API 参数提取模块。










