应用场景
- 序列化和反序列化,比如 json、protobuf 等各种数据协议
- 各种数据库的 ORM, 比如 gorm、sqlx 等数据库中间件
- 配置文件解析相关的库,比如yaml、ini
使用反射获取结构体的成员类型
任意值通过 reflect.TypeOf() 获得反射对象信息后,如果它的类型是结构体,可以通过反射值对象 reflect.Type 的 NumField() 和 Field() 方法获得结构体成员的详细信息。
与成员获取相关的 reflect.Type 的方法如下表所示。
| 方法 | 说明 |
|---|---|
| Field(i int) StructField | 根据索引返回索引对应的结构体字段的信息,当值不是结构体或索引超界时发生宕机 |
| NumField() int | 返回结构体成员字段数量,当类型不是结构体或索引超界时发生宕机 |
| FieldByName(name string) (StructField, bool) | 根据给定字符串返回字符串对应的结构体字段的信息,没有找到时 bool 返回 false,当类型不是结构体或索引超界时发生宕机 |
| FieldByIndex(index []int) StructField | 多层成员访问时,根据 []int 提供的每个结构体的字段索引,返回字段的信息,没有找到时返回零值。当类型不是结构体或索引超界时发生宕机 |
| FieldByNameFunc(match func(string) bool) (StructField,bool) | 根据匹配函数匹配需要的字段,当值不是结构体或索引超界时发生宕机 |
结构体字段类型
reflect.Type 的 Field() 方法返回 StructField 结构,这个结构描述结构体的成员信息,通过这个信息可以获取成员与结构体的关系,如偏移、索引、是否为匿名字段、结构体标签(StructTag)等,而且还可以通过 StructField 的 Type 字段进一步获取结构体成员的类型信息。
StructField 的结构如下:
type StructField struct {Name string // 字段名PkgPath string // 字段路径Type Type // 字段反射类型对象Tag StructTag // 字段的结构体标签Offset uintptr // 字段在结构体中的相对偏移Index []int // Type.FieldByIndex中的返回的索引值Anonymous bool // 是否为匿名字段}
package mainimport ("fmt""reflect")func main() {// 声明一个空结构体type cat struct {Name string// 带有结构体tag的字段Type int `json:"type" id:"100"`}// 创建cat的实例ins := cat{Name: "mimi", Type: 1}// 获取结构体实例的反射类型对象typeOfCat := reflect.TypeOf(ins)// 遍历结构体所有成员for i := 0; i < typeOfCat.NumField(); i++ {// 获取每个成员的结构体字段类型fieldType := typeOfCat.Field(i)// 输出成员名和tagfmt.Printf("name: %v tag: '%v'\n", fieldType.Name, fieldType.Tag)}// 通过字段名, 找到字段类型信息if catType, ok := typeOfCat.FieldByName("Type"); ok {// 从tag中取出需要的tagfmt.Println(catType.Tag.Get("json"), catType.Tag.Get("id"))}}/*name: Name tag: ''name: Type tag: 'json:"type" id:"100"'type 100*/
获取结构体的tag标记的值
/**string:根据 Tag 中的键获取对应的值例如`key1:"value1" key2:"value2"`的 Tag 中,可以传入“key1”获得“value1”。*/func (tag StructTag) Get(key string)// 根据 Tag 中的键,查询值是否存在。func (tag StructTag) Lookup(key string) (value string, ok bool)
package mainimport ("fmt""reflect")func main() {type cat struct {Name string `yaml:"name" required:"true"`Type int `yaml:"type" id:"100"`}typeOfCat := reflect.TypeOf(cat{})if catType, ok := typeOfCat.FieldByName("Type"); ok {fmt.Println(catType.Tag.Get("yaml")) // typefmt.Println(catType.Tag.Get("id")) // 100}if catType, ok := typeOfCat.FieldByName("Name"); ok {fmt.Println(catType.Tag.Get("yaml")) // namevalue, isOK := catType.Tag.Lookup("test")fmt.Println("value:", value, ", isOK:", isOK) // value: , isOK: false}}
