四、从反射值对象获取结构体字段的值
如果变量是结构体类型,那么使用 ValueOf
函数返回的 reflect.Value
类型有以下几种方法可以获取结构体中的字段的值
方法名以及返回值类型 | 方法说明 |
Field(i int) Value | 根据索引,返回对应结构体字段的 reflect.Value 对象,接着可以再获取到字段的类型和值。当值不是结构体或者索引越界会引发 panic |
NumberField() int | 返回结构体成员字段数量,当值不是结构体或者索引越界会引发 panic |
FieldByName(name string) Value | 通过字段名获取指定字段的反射值对象,没有找到时返回零值,当值不是结构体或者索引越界会引发 panic |
FieldByIndex(index []int) Value | 多层成员访问时,通过索引切片中的索引一层层获取指定索引的反射值对象,没有找到时返回零值,当值不是结构体或者索引越界会引发 panic |
FieldByNameFunc(match func(string) bool) Value | 根据匹配函数匹配需要的字段,没有找到时返回零值,当值不是结构体或者索引越界会引发 panic |
package main import ( "fmt" "reflect" ) func main(){ t := Teacher{"Stark", 33, "NYC"} s := Stu{"Peter", 18, "HighSchool","M", t} stuValueOf := reflect.ValueOf(s) fmt.Printf("stuValueOf 的类型为:%T\n", stuValueOf) // 结构体字段个数 fmt.Printf("通过结构体反射值对象获取到结构体的字段个数为:%v\n", stuValueOf.NumField()) // 使用不同方法获取结构体字段的反射值对象 stuValueOfName := stuValueOf.FieldByName("Name") stuValueOfAge := stuValueOf.Field(1) stuValueOfTeaName := stuValueOf.FieldByIndex([]int{4, 0}) fmt.Printf("stuValueOfName 的类型为:%T\n", stuValueOfName) fmt.Printf("stuValueOfAge 的类型为:%T\n", stuValueOfAge) fmt.Printf("stuValueOfTeaName 的类型为:%T\n", stuValueOfTeaName) // 再从字段的反射值对象获取原值,可以使用 Interface() 返回获取到接口类型在通过接口断言转换为具体类型 // 也可以直接通过 String() Int() Float() Bool() 等方法直接进行转换 stuName := stuValueOfName.String() stuAge := stuValueOfAge.Interface().(int) // 强制类型转换,从 int64 转为 int stuTeaName := stuValueOfTeaName.String() fmt.Printf("从 reflect.Value 反射值对象获取的原 s 实例化结构体的 Name 字段的值为:%v, 类型为:%T\n", stuName, stuName) fmt.Printf("从 reflect.Value 反射值对象获取的原 s 实例化结构体的 Age 字段的值为:%v, 类型为:%T\n", stuAge, stuAge) fmt.Printf("从 reflect.Value 反射值对象获取的原 s 实例化结构体的 Teacher 字段(结构体) 中的 Name 的值为:%v, 类型为:%T\n", stuTeaName, stuTeaName) } type Stu struct { Name string `json:"name"` Age int `json:"age"` Grade string `json:"grade"` Gender string `json:"gender"` Teacher } type Teacher struct { Name string `json:"name"` Age int `json:"age"` Address string `json:"address"` } 复制代码
执行上述代码,输出结果如下:
stuValueOf 的类型为:reflect.Value 通过结构体反射值对象获取到结构体的字段个数为:5 stuValueOfName 的类型为:reflect.Value stuValueOfAge 的类型为:reflect.Value stuValueOfTeaName 的类型为:reflect.Value 从 reflect.Value 反射值对象获取的原 s 实例化结构体的 Name 字段的值为:Peter, 类型为:string 从 reflect.Value 反射值对象获取的原 s 实例化结构体的 Age 字段的值为:18, 类型为:int 从 reflect.Value 反射值对象获取的原 s 实例化结构体的 Teacher 字段(结构体) 中的 Name 的值为:Stark, 类型为:string 复制代码
需要注意的是在使用 FieldByName
方法获取指定名字的结构体字段时,入参要写大写的字段名,不要写 json 标签中的字段名。
五、反射值对象空判断和有效性判断
上篇文章中讲到了在获取了结构体字段的反射值对象或者是基本数据类型变量的反射值对象之后获取原数据的操作,但是在获取之前为避免报错可以先进行值是否为空判断或者有效性的判断。
值是否为空判断和有效性判断需要用到 reflect.Value
反射值对象的下面两个方法:
方法名以及返回值 | 方法说明 |
IsNil() bool | 返回值是否为 nil,如果值类型不是通道 channel、函数、接口、map、指针或者切片时会发生 panic 错误 |
IsValid() bool | 判断值是否有效,当值本身非法时,返回 false,如果返回值对象不包含任何值,值为 nil |
package main import ( "fmt" "reflect" ) func main() { // 判断结构体指针的反射值对象是否有效 var zulu *int zuluValueOf := reflect.ValueOf(zulu) fmt.Printf("Int 指针变量 zulu 的反射值对象是否为空:%v\n", zuluValueOf.IsNil()) fmt.Printf("Int 指针变量 zulu 的反射值对象是否有效:%v\n", zuluValueOf.IsValid()) // 空指针 fmt.Printf("Int 指针变量 zulu 通过反射值对象获取的 zulu 指针变量指向的对象是否有效:%v\n", zuluValueOf.Elem().IsValid()) // 判断 Map 的 键是否有效 m := map[string]string{} mValueOf := reflect.ValueOf(m) // 通过字符串类型的键 name 的 反射值对象获取该键对应的值的反射值对象 kValueOf := mValueOf.MapIndex(reflect.ValueOf("name")) fmt.Printf("m 变量中键 name 对应的值的反射值对象是否有效:%v\n", kValueOf.IsValid()) // 实例化一个 Teacher 结构体 t := Teacher{"Stark", 33, "NYC"} tValueOf := reflect.ValueOf(t) // 获取结构体中不存在的字段 balanceValueOf := tValueOf.FieldByName("Balance") methodValueOf := tValueOf.MethodByName("Teach") fmt.Printf("t 结构体实例化对象的反射值获取的 balance 字段的反射值是否有效:%v\n", balanceValueOf.IsValid()) fmt.Printf("t 结构体实例化对象的反射值获取的 Teach 结构体方法是否有效:%v\n", methodValueOf.IsValid()) } type Teacher struct { Name string `json:"name"` Age int `json:"age"` Address string `json:"address"` } 复制代码
执行上述代码,输出结果如下:
Int 指针变量 zulu 的反射值对象是否为空:true Int 指针变量 zulu 的反射值对象是否有效:true Int 指针变量 zulu 通过反射值对象获取的 zulu 指针变量指向的对象是否有效:false m 变量中键 name 对应的值的反射值对象是否有效:false t 结构体实例化对象的反射值获取的 balance 字段的反射值是否有效:false t 结构体实例化对象的反射值获取的 Teach 结构体方法是否有效:false