当前位置:网站首页>Teach you how to use the reflect package to parse the structure of go - step 1: parameter type check
Teach you how to use the reflect package to parse the structure of go - step 1: parameter type check
2022-06-24 08:09:00 【amc】
Go Native encoding/json Of Unmarshal and Marshal The input parameter of the function is interface{}, And can support any struct or map type . This functional pattern , How to achieve it specifically ? This article will briefly explore the basis of this implementation pattern :reflect package .
Basic concepts
interface{}
Beginners Go, You will soon come into contact with Go A special type of :interface.Interface The meaning is : Implement the specified interface All types of functions defined in the body . for instance , We have the following interface definitions :
type Dog interface{
Woof()
} So as long as it is realized Woof() function ( bark ), Can be considered to be achieved Dog Type of interface . Be careful , It's all types of , Not limited to complex types or basic types . For example, we use int Redefine a type , It's OK, too :
type int FakeDog
func (d FakeDog) Woof() {
// do hothing
} good , Next , We will see a common way of writing :interface{},interface The word is followed by a curly bracket that contains nothing . We need to know ,Go Supports anonymous types , So this is still an interface type , This interface does not specify any functions that need to be implemented .
So from the semantic point of view, we can know , Any type conforms to the definition of this interface . On the other hand ,interface{} It can be used to express Any type . This is it. json marshaling and unmarshaling Input .
reflect
OK, Although there are interface{} Used to represent “ Any type ”, But eventually we have to parse this “ Any type ” Parameter bar ?Go Provides reflect package , Used to resolve . This is what is often mentioned in Chinese materials “ The reflex mechanism ”. Reflection can do many things , In this article, we mainly deal with the part of parsing structure .
following , We set up an experiment / Application scenarios , Step by step reflect Usage and precautions .
Experimental scenario
Various mainstream serialization / The deserialization protocol is as follows json、yaml、xml、pb There are authoritative and official libraries for everything ; But in the URL query scenario , It is not particularly perfect yet . Let's play with this scene —— URL query and struct Interturn .
First we define a function :
func Marshal(v interface{}) ([]byte, error) Internal implementation , The logic is to parse the field information of the input parameter first , Turn into native url.Values type , Then call Encode Function to byte string output , In this way, we don't have to worry about the escape of special characters .
func Marshal(v interface{}) ([]byte, error) {
kv, err := marshalToValues(v)
if err != nil {
return nil, err
}
s := kv.Encode()
return []byte(s), nil
}
func marshalToValues(in interface{}) (kv url.Values, err error) {
// ......
}Input parameter type check —— reflect.Type
First we see , The input is one interface{}, That is to say “ Any type ”. Ostensibly of any type , But in fact, not all data types support conversion , So here we need to check the input parameter type .
Here we come across the first data type we need to know :reflect.Type.reflect.Type adopt reflect.TypeOf(v) Or is it reflect.ValueOf(v).Type() get , This type contains all the data type related information of the input parameter :
func marshalToValues(in interface{}) (kv url.Values, err error) {
if in == nil {
return nil, errors.New("no data provided")
}
v := reflect.ValueOf(in)
t := v.Type()
// ......
} According to the demand , The input parameters we allow are structs or struct pointers . Here's what I'm using reflect.Kind type .
Kind and type What's the difference ? First we know that ,Go It's a strongly typed language ( super !), Use type newType oldType The two types defined by such a statement , Although you can use explicit type conversion , But assign directly 、 operation 、 Compare and so on , It's impossible to pass , It may even cause panic:
package main
import "fmt"
func main() {
type str string
s1 := str("I am a str")
s2 := "I am a string"
fmt.Println(s1 == s2)
}
// go run Unable to get , The compilation information is :
// ./main.go:9:17: invalid operation: s1 == s2 (mismatched types str and string) here , We said str and string Of type Is different . But we can say ,str and string Of kind It's the same , Why? ?Godoc Yes Kind The description of is :
- A Kind represents the specific kind of type that a Type represents. The zero Kind is not a valid kind.
Be careful “kind of type”,kind It's right type Further classification of ,Kind Covers all Go data type , adopt Kind, We can know what the underlying type of a variable is .Kind Is an enumeration value , Here is the complete list :
reflect.Invaid: Indicates that it is not a legal type valuereflect.Bool: Boolean value , arbitrarilytype xxx boolEven a further definition in series , It's all this kind. The following is similar to .reflect.Int,reflect.Int64,reflect.Int32,reflect.Int16,reflect.Int8: Various signed integer types . Strictly speaking, these types of kind All different , But it can often be handled together . The reason will be mentioned later .reflect.Uint,reflect.Uint64,reflect.Uint32,reflect.Uint16,reflect.Uint8: Various unsigned integer types .reflect.Uintptr:uintptrtypereflect.Float32,reflect.Float64: Floating point typereflect.Complex32,reflect.Complex64: Plural typereflect.Array: An array type . Note the difference from the slicereflect.Chan: Go channel typereflect.Func: functionreflect.Interface:interfacetype . Naturally ,interface{}Also of this typereflect.Map:maptypereflect.Ptr: Pointer typesreflect.Slice: Slice type . Notice the difference from the arrayreflect.String:stringtypereflect.Struct: Type of structurereflect.UnsafePointer:unsafe.Pointertype
It seems a little dizzy ? No problem , Let's do the simplest check here —— At this stage, we check the input parameters of the entire function , Only structs or pointer types are allowed , Nothing else is allowed .OK, Our input parameter check can be written as follows :
func marshalToValues(in interface{}) (kv url.Values, err error) {
// ......
v := reflect.ValueOf(in)
t := v.Type()
if k := t.Kind(); k == reflect.Struct || k == reflect.Ptr {
// OK
} else {
return nil, fmt.Errorf("invalid type of input: %v", t)
}
// ......
}The entry inspection is not finished yet . If the input parameter is a struct, So good , We can rub our hands . But if the input parameter is a pointer , Need to know , A pointer can be a pointer to any data type , So we also need to check the type of the pointer .
If the input parameter is a pointer , We can jump to reflect.Type Of Elem() function , Get it as a pointer , The type of data pointed to . Then we can check this type .
This time, , We are only allowed to point to one structure , meanwhile , The value of this structure cannot be nil. Now , The code for entering parameter validity check is quite long , Let's draw the validity check into a special function . So the above function fragment , We rewrite it like this :
func marshalToValues(in interface{}) (kv url.Values, err error) {
v, err := validateMarshalParam(in)
if err != nil {
return nil, err
}
// ......
}
func validateMarshalParam(in interface{}) (v reflect.Value, err error) {
if in == nil {
err = errors.New("no data provided")
return
}
v = reflect.ValueOf(in)
t := v.Type()
if k := t.Kind(); k == reflect.Struct {
// struct type , That's good , Go straight back to
return v, nil
} else if k == reflect.Ptr {
if v.IsNil() {
// Pointer types , Value is empty , That's even if it's struct type , Can't parse
err = errors.New("nil pointer of a struct is not supported")
return
}
// Check whether the type pointed to by the pointer is struct
t = t.Elem()
if t.Kind() != reflect.Struct {
err = fmt.Errorf("invalid type of input: %v", t)
return
}
return v.Elem(), nil
}
err = fmt.Errorf("invalid type of input: %v", t)
return
}Input parameter value iteration —— reflect.Value
From the previous function , We came across the second data type that we need to know :reflect.Value.reflect.Value adopt reflect.ValueOf(v) get , This type contains all the information about the target parameter , It also contains the corresponding reflect.Type. In the entry inspection stage , We only cover its three functions :
Type(): getreflect.TypevalueElem(): When the variable is of pointer type , Then the correspondingreflect.ValuevalueIsNil(): When the variable is of pointer type , You can determine whether the value is empty . In fact, you can also skipIsNilThe logic goes on , So int = t.Elem()Back , getreflect.Invalidvalue .
next step
This article has entered a door , I checked interface{} type . Next we need to explore reflect.Value The internal members of the structure of the format , Coming soon . Besides , The code of this article can also be found in Github Found on the , The codes in this phase correspond to Commit 915e331.
Reference material
- Checking reflect.Kind on interface{} return invalid result
- Go reflection Three laws and best practices - 3. reflect.Value data structure
Other articles recommend
- Go Language design and implementation - 4.3 Reflection
- It's still in use map[string]interface{} Handle JSON? Tell you a more efficient way ——jsonvalue
- Go Language native json What's wrong with the bag ? How to better handle JSON data ?
- I'll teach you how to use reflect Package parsing Go The structure of the body - Step 2: Structure member traversal
- hand Teach you how to use reflect Package parsing Go The structure of the body - Step 3: Complex type checking
This article adopts Creative Commons signature - Noncommercial use - Share in the same way 4.0 International licensing agreement Licensing .
The original author : amc, Welcome to reprint , But please note the source .
Original title :《 I'll teach you how to use reflect Package parsing Go The structure of the body - Step 1: Parameter type check 》
Release date :2021-06-28
Link to the original text :https://cloud.tencent.com/developer/article/1839823
边栏推荐
- 慕思股份在深交所上市:毛利率持续下滑,2022年一季度营销失利
- Error "computing failed in `stat\u summary\u hex() `"
- 4-operation list (loop structure)
- Case examples of corpus data processing (cases related to sentence retrieval)
- Cold thinking on the hot track: multiplier effect is the fundamental requirement of East West calculation
- Leetcode 515 find the leetcode path of the maximum [bfs binary tree] heroding in each row
- redolog和binlog
- Serialization of unity
- Swift Extension NetworkUtil(網絡監聽)(源碼)
- 闲谈:3AC到底发生了什么?
猜你喜欢

1279_VMWare Player安装VMWare Tools时VSock安装失败解决

Swift Extension NetworkUtil(網絡監聽)(源碼)

『C语言』系统日期&时间

Selenium IDE的安装以及使用

Hongmeng OS development III

不止于观测|阿里云可观测套件正式发布

Leetcode 207: course schedule (topological sorting determines whether the loop is formed)

Hilbert Huang Transform

单片机STM32F103RB,BLDC直流电机控制器设计,原理图、源码和电路方案

On the H5 page, the Apple phone blocks the content when using fixed to locate the bottom of the tabbar
随机推荐
Duilib display memory picture
C# Lambda
ImportError: cannot import name ‘process_pdf‘ from ‘pdfminer.pdfinterp‘错误完全解决
From jsonpath and XPath to spl
Leetcode 207: course schedule (topological sorting determines whether the loop is formed)
第 3 篇:绘制三角形
Thread blocking
GraphMAE----論文快速閱讀
Examples of corpus data processing cases (reading multiple text files, reading multiple files specified under a folder, decoding errors, reading multiple subfolder text, batch renaming of multiple fil
Signature analysis of app x-zse-96 in a Q & a community
研究生英语期末考试复习
单片机STM32F103RB,BLDC直流电机控制器设计,原理图、源码和电路方案
Chrono usage notes
Do you still have the opportunity to become a machine learning engineer without professional background?
Selenium IDE的安装以及使用
第 2 篇:繪制一個窗口
The monthly salary of two years after graduation is 36K. It's not difficult to say
Pagoda panel installation php7.2 installation phalcon3.3.2
How to cancel the display of the return button at the uniapp uni app H5 end the autobackbutton does not take effect
Backup and restore SQL Server Databases locally