应用场景

  • 序列化和反序列化,比如 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 的结构如下:

  1. type StructField struct {
  2. Name string // 字段名
  3. PkgPath string // 字段路径
  4. Type Type // 字段反射类型对象
  5. Tag StructTag // 字段的结构体标签
  6. Offset uintptr // 字段在结构体中的相对偏移
  7. Index []int // Type.FieldByIndex中的返回的索引值
  8. Anonymous bool // 是否为匿名字段
  9. }
  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. func main() {
  7. // 声明一个空结构体
  8. type cat struct {
  9. Name string
  10. // 带有结构体tag的字段
  11. Type int `json:"type" id:"100"`
  12. }
  13. // 创建cat的实例
  14. ins := cat{Name: "mimi", Type: 1}
  15. // 获取结构体实例的反射类型对象
  16. typeOfCat := reflect.TypeOf(ins)
  17. // 遍历结构体所有成员
  18. for i := 0; i < typeOfCat.NumField(); i++ {
  19. // 获取每个成员的结构体字段类型
  20. fieldType := typeOfCat.Field(i)
  21. // 输出成员名和tag
  22. fmt.Printf("name: %v tag: '%v'\n", fieldType.Name, fieldType.Tag)
  23. }
  24. // 通过字段名, 找到字段类型信息
  25. if catType, ok := typeOfCat.FieldByName("Type"); ok {
  26. // 从tag中取出需要的tag
  27. fmt.Println(catType.Tag.Get("json"), catType.Tag.Get("id"))
  28. }
  29. }
  30. /*
  31. name: Name tag: ''
  32. name: Type tag: 'json:"type" id:"100"'
  33. type 100
  34. */

获取结构体的tag标记的值

  1. /**
  2. string:根据 Tag 中的键获取对应的值
  3. 例如`key1:"value1" key2:"value2"`的 Tag 中,可以传入“key1”获得“value1”。
  4. */
  5. func (tag StructTag) Get(key string)
  6. // 根据 Tag 中的键,查询值是否存在。
  7. func (tag StructTag) Lookup(key string) (value string, ok bool)
  1. package main
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. func main() {
  7. type cat struct {
  8. Name string `yaml:"name" required:"true"`
  9. Type int `yaml:"type" id:"100"`
  10. }
  11. typeOfCat := reflect.TypeOf(cat{})
  12. if catType, ok := typeOfCat.FieldByName("Type"); ok {
  13. fmt.Println(catType.Tag.Get("yaml")) // type
  14. fmt.Println(catType.Tag.Get("id")) // 100
  15. }
  16. if catType, ok := typeOfCat.FieldByName("Name"); ok {
  17. fmt.Println(catType.Tag.Get("yaml")) // name
  18. value, isOK := catType.Tag.Lookup("test")
  19. fmt.Println("value:", value, ", isOK:", isOK) // value: , isOK: false
  20. }
  21. }