头图

We often encounter conversions between strings and structures during development. For example, when calling APIs, we need to convert JSON strings into struct structures. Specifically how to perform the conversion, you need to use reflection .

reflection

For reflection, the previous article has already introduced it, Portal: "Assertion, Understanding and Use of Reflection! " , here we will explain the reflection in depth.

reflect.Value and reflect.Type

There are two types of reflection, reflect.Value and reflect.Type , which represent the value and type of the variable, and two functions reflect.ValueOf and reflect.TypeOf are provided to obtain the reflect.Value and reflect.Type any object, respectively.

reflect.Value

reflect.Value can be obtained by function reflect.ValueOf . reflect.Value is defined as a struct structure:

type Value struct {
   typ *rtype
   ptr unsafe.Pointer
   flag
}

As you can see, the fields in this structure are all private. We can only use the reflect.Value method. Its methods are:

//针对具体类型的系列方法
//以下是用于获取对应的值
Bool
Bytes
Complex
Float
Int
String
Uint
CanSet //是否可以修改对应的值
//以下是用于修改对应的值
Set
SetBool
SetBytes
SetComplex
SetFloat
SetInt
SetString
Elem //获取指针指向的值,一般用于修改对应的值
//以下Field系列方法用于获取struct类型中的字段
Field
FieldByIndex
FieldByName
FieldByNameFunc
Interface //获取对应的原始类型
IsNil //值是否为nil
IsZero //值是否是零值
Kind //获取对应的类型类别,比如Array、Slice、Map等
//获取对应的方法
Method
MethodByName
NumField //获取struct类型中字段的数量
NumMethod//类型上方法集的数量
Type//获取对应的reflect.Type

The methods are divided into three categories:

  1. Get and modify the corresponding value
  2. The fields of struct type are related to obtain the corresponding fields
  3. The method set on the type is related to obtain the corresponding method

Convert any type of object and reflect.Value:

func main() {
   i := 5
   //int to reflect.Value
   iv:=reflect.ValueOf(i)
   //reflect.Value to int
   i1 := iv.Interface().(int)
   fmt.Println(i1)
}
Modify the corresponding value
func main() {
   i := 5
   ipv := reflect.ValueOf(&i)
   ipv.Elem().SetInt(6)
   fmt.Println(i)
}
  • In the example, we modified a variable through reflection
  • The reflect.ValueOf function returns a copy of the value, so we have to pass in the pointer of the variable.
  • Because what is passed is a pointer, you need to call the Elem method to find the value pointed to by this pointer, so that you can modify it
  • To modify the value of a variable, the key point: pass a pointer (addressable), and obtain the pointed value through the Elem method to ensure that the value can be modified. Reflect.Value provides us with the CanSet method to determine whether the variable can be modified.
Modify the value of the struct structure field
func main() {
   p := person{Name: "微客鸟窝",Age: 18}
   pv:=reflect.ValueOf(&p)
   pv.Elem().Field(0).SetString("无尘")
   fmt.Println(p)
}
type person struct {
   Name string
   Age int
}

Summary of steps:

  1. Pass a pointer to a struct structure to obtain the corresponding reflect.Value;
  2. Obtain the value pointed to by the pointer through the Elem method;
  3. Get the field to be modified through the Field method;
  4. Modify it to the corresponding value through the Set series method.

    Rules for modifying a value through reflection:

    • It can be addressed, in layman's terms, is to pass a pointer as a parameter to the reflect.ValueOf function.
    • If you want to modify the value of a struct structure field, the field needs to be exportable, not private, that is, the first letter of the field is capitalized.
    • Remember to use the Elem method to get the value pointed to by the pointer, so that you can call the Set series methods to modify it.

Get the corresponding underlying type

Because we can customize some new types through the type keyword, and the underlying types are some basic types. For example, the p variable type in the above example is person, and the underlying type is struct structure type.

func main() {
   p := person{Name: "微客鸟窝",Age: 18}
   pv:=reflect.ValueOf(&p)
   fmt.Println(pv.Kind())
   pv:=reflect.ValueOf(p)
   fmt.Println(pv.Kind())
}

//运行结果:
ptr
struct

The Kind method returns a Kind type value, which is a constant. From the source code, the list of defined Kind constants contains all the underlying types of the Go language:

type Kind uint
const (
   Invalid Kind = iota
   Bool
   Int
   Int8
   Int16
   Int32
   Int64
   Uint
   Uint8
   Uint16
   Uint32
   Uint64
   Uintptr
   Float32
   Float64
   Complex64
   Complex128
   Array
   Chan
   Func
   Interface
   Map
   Ptr
   Slice
   String
   Struct
   UnsafePointer
)

reflect.Type

  • reflect.Type is an interface, not a structure, so you can only use its methods.
  • Commonly used methods of reflect.Type interface

    type Type interface {
    
     Implements(u Type) bool //方法用于判断是否实现了接口 u;
     AssignableTo(u Type) bool //方法用于判断是否可以赋值给类型 u,其实就是是否可以使用 =,即赋值运算符;
     ConvertibleTo(u Type) bool //方法用于判断是否可以转换成类型 u,其实就是是否可以进行类型转换;
     Comparable() bool //方法用于判断该类型是否是可比较的,其实就是是否可以使用关系运算符进行比较。
    
     //以下这些方法和Value结构体的功能相同
     Kind() Kind
    
     Method(int) Method
     MethodByName(string) (Method, bool)
     NumMethod() int
     Elem() Type
     Field(i int) StructField
     FieldByIndex(index []int) StructField
     FieldByName(name string) (StructField, bool)
     FieldByNameFunc(match func(string) bool) (StructField, bool)
     NumField() int
    }

Traverse the fields and methods of the structure

package main

import (
    "fmt"
    "reflect"
)

func main() {
    p := person{Name: "微客鸟窝", Age: 18}
    pt := reflect.TypeOf(p)
    //遍历person的字段
    for i := 0; i < pt.NumField(); i++ {
        fmt.Println("字段:", pt.Field(i).Name)
    }
    //遍历person的方法
    for i := 0; i < pt.NumMethod(); i++ {
        fmt.Println("方法:", pt.Method(i).Name)
    }
}

type person struct {
    Name string
    Age  int
}

func (p person) String() string{
    return fmt.Sprintf("Name is %s,Age is %d",p.Name,p.Age)
}

operation result:

字段: Name
字段: Age
方法: String

Whether to implement an interface

//....
func main() {
    p := person{Name: "微客鸟窝", Age: 20}
    pt := reflect.TypeOf(p)
    stringerType := reflect.TypeOf((*fmt.Stringer)(nil)).Elem()
    writerType := reflect.TypeOf((*io.Writer)(nil)).Elem()
    fmt.Println("是否实现了fmt.Stringer:", pt.Implements(stringerType))
    fmt.Println("是否实现了io.Writer:", pt.Implements(writerType))
}
//...

operation result:

是否实现了fmt.Stringer: true
是否实现了io.Writer: false

JSON and Struct conversion

We can convert between JSON and Struct through the json package in the standard library:

//......
func main() {
    p := person{Name: "微客鸟窝", Age: 18}
    //struct to json
    jsonB, err := json.Marshal(p)
    if err == nil {
        fmt.Println(string(jsonB))
    }
    //json to struct
    respJSON := "{\"Name\":\"无尘\",\"Age\":18}"
    json.Unmarshal([]byte(respJSON), &p)
    fmt.Println(p)
}
//......

operation result:

{"Name":"微客鸟窝","Age":18}
Name is 无尘,Age is 18
  • Through the json.Marshal function, struct can be converted into a JSON string.
  • Through the json.Unmarshal function, you can convert a JSON string to a struct.

Struct Tag

The struct tag is a tag added to the struct field, use it for assistance. If you want to get the tag on the field, you must first reflect the corresponding field, which can be done through the Field method. This method returns a StructField structure, which has a field of Tag, which stores all tags of the field.
Example:

//遍历person字段中key为json、bson的tag
for i:=0;i<pt.NumField();i++{
   sf:=pt.Field(i)
   fmt.Printf("字段%s上,json tag为%s\n",sf.Name,sf.Tag.Get("json"))
   fmt.Printf("字段%s上,bson tag为%s\n",sf.Name,sf.Tag.Get("bson"))
}
type person struct {
   Name string `json:"name" bson:"b_name"`
   Age int `json:"age" bson:"b_name"`
}

operation result:

字段Name上,key为json的tag为name
字段Name上,key为bson的tag为b_name
字段Age上,key为json的tag为age
字段Age上,key为bson的tag为b_name

Law of reflection

The author of the Go language summarized the three laws of reflection on his blog:

  1. Any interface value interface{} can reflect the reflection object, that is, reflect.Value and reflect.Type, which are obtained through the functions reflect.ValueOf and reflect.TypeOf.
  2. The reflection object can also be reduced to the interface{} variable, which is the reversibility of the first law, obtained through the Interface method of the reflect.Value structure.
  3. To modify the reflected object, the value must be settable, that is, addressable. Refer to the section on modifying the value of the variable in the previous lesson for understanding.

微客鸟窝
37 声望3 粉丝