
本文介绍如何使用反射(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
输出结果:
非空字段名:Name Token
✅ 关键说明:
- empty() 函数覆盖了常见类型(字符串、数值、布尔、指针、切片、映射等)的零值判断逻辑;
- 对 struct 类型未做深度判空(因其不可为 nil),若需严格检查嵌套结构体是否全为零值,可递归调用 empty();
- 注意:reflect.ValueOf(u) 传入的是值拷贝,因此无法检测指针字段本身的 nil 状态以外的深层内容(如 *string 指向空字符串仍算“非空”,但其解引用值为空——这取决于业务语义);
- 若结构体含导出字段(首字母大写)且需支持 JSON 标签映射等,可进一步结合 StructField.Tag 提取自定义标识(如 json:"name,omitempty")。
此方法轻量、通用,适合构建灵活的数据校验、序列化过滤或动态 API 参数提取模块。