2
头图

Original link: What does it mean to tag a structure in Go language?

Preface

Hello, everyone, my name is asong . Today I want to share with you Go language, and how to customize your own structure tag analysis.

When most beginners look at the company's project code, some structure definitions they see will look like this:

type Location struct {
    Longitude float32 `json:"lon,omitempty"`
    Latitude  float32 `json:"lat,omitempty"`
}

There will be a label after the field. What is the use of this label?

In the above example, the label json:"lon,omitempty" means that when the value of the structure field is encoded as the json object, each derived field becomes a member of the object. The name of this member is lon or lat , and when the field is null , Do not export this field; the summary is lon , lat is the name of the renamed member, and omitempty used to determine whether the member is exported.

Seeing this, some friends may be curious, how did you know how to use this? Can I write labels casually?

Next, we will reveal the secret a little bit, and drive! ! !

What is a label

Go language provides structure tags that can be discovered through reflection. These are widely used json/xml orm framework also supports structure tags. The above example is used because encoding/json supports structure tags, but He has his own labeling rules; but they all have a general rule, this rule cannot be changed, the specific format is as follows:

`key1:"value1" key2:"value2" key3:"value3"...`  // 键值对用空格分隔

Structure tags can have multiple key-value pairs. Keys and values must be separated by colons, values must be enclosed in double quotation marks, and multiple key-value pairs must be separated by a space. Do not use commas! ! !

What if we want to pass multiple information in one value? The implementations in different libraries are different. In encoding/json , multiple values are separated by commas:

`json:"lon,omitempty"`

In gorm , multiple values are separated by semicolons:

`gorm:"column:id;primaryKey"

You need to see the documentation of the respective library to obtain the specific symbol separation.

The structure tag is associated with the member during the compilation phase, and is associated in the form of a string, which can be read out through reflection during the runtime phase.

Now that everyone knows what structure tags are, the rules are still very standardized, but it is easy to make mistakes, because the Go language does not check its format for legal key-value pairs during the compilation phase, so we accidentally wrote it wrong. It is difficult to be found, but we have the go vet look at an example for specific use:

type User struct {
    Name string `abc def ghk`
    Age uint16 `123: 232`
}
func main()  {
}

Then execute go vet main.go to get the execution result:

# command-line-arguments
go_vet_tag/main.go:4:2: struct field tag `abc def ghk` not compatible with reflect.StructTag.Get: bad syntax for struct tag pair
go_vet_tag/main.go:5:2: struct field tag `123: 232` not compatible with reflect.StructTag.Get: bad syntax for struct tag value

bad syntax for struct tag pair tells us that the key-value pair has a syntax error, and the bad syntax for struct tag value value has a syntax error.

Therefore, it is necessary go vet as the CI

Label usage scenarios

Go official has helped sort out which libraries already support struct tag : https://github.com/golang/go/wiki/Well-known-struct-tags.

TagDocumentation
xmlhttps://godoc.org/encoding/xml
jsonhttps://godoc.org/encoding/json
asn1https://godoc.org/encoding/asn1
reformhttps://godoc.org/gopkg.in/reform.v1
dynamodbhttps://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/dynamodbattribute/#Marshal
bigqueryhttps://godoc.org/cloud.google.com/go/bigquery
datastorehttps://godoc.org/cloud.google.com/go/datastore
spannerhttps://godoc.org/cloud.google.com/go/spanner
bsonhttps://godoc.org/labix.org/v2/mgo/bson, https://godoc.org/go.mongodb.org/mongo-driver/bson/bsoncodec
gormhttps://godoc.org/github.com/jinzhu/gorm
yamlhttps://godoc.org/gopkg.in/yaml.v2
tomlhttps://godoc.org/github.com/pelletier/go-toml
validatehttps://github.com/go-playground/validator
mapstructurehttps://godoc.org/github.com/mitchellh/mapstructure
parserhttps://godoc.org/github.com/alecthomas/participle
protobufhttps://github.com/golang/protobuf
dbhttps://github.com/jmoiron/sqlx
urlhttps://github.com/google/go-querystring
featurehttps://github.com/nikolaydubina/go-featureprocessing

Like json , yaml , gorm , validate , mapstructure , protobuf structure label these libraries is very common, gin framework to integrate validate library used for parameter validation, a lot of convenience, before writing an article about validate The article: use the validator library for data verification. I opened ~~~ , you can pay attention to it.

For details on how these libraries are used, you can read the official documentation. They are written in detail, and they are used in specific scenarios! ! !

Custom structure label

Now we can answer the first question. The structure tag can be written at will, as long as it conforms to the grammatical rules, it can be written arbitrarily, but if some libraries do not support this tag, the tag written at will has no meaning. , If we want our tags to become meaningful, we need to provide analysis methods. The label can be obtained by reflection, so let's look at an example of how to use reflection to obtain a custom structure label.

type User struct {
    Name string `asong:"Username"`
    Age  uint16 `asong:"age"`
    Password string `asong:"min=6,max=10"`
}
func getTag(u User) {
    t := reflect.TypeOf(u)

    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        tag := field.Tag.Get("asong")
        fmt.Println("get tag is ", tag)
    }
}

func main()  {
    u := User{
        Name: "asong",
        Age: 5,
        Password: "123456",
    }
    getTag(u)
}

The results of the operation are as follows:

get tag is  Username
get tag is  age
get tag is  min=6,max=10

Here we use TypeOf method, and then traverse the fields, each field StructField has a member variable Tag :

// A StructField describes a single field in a struct.
type StructField struct {
    Name string
    PkgPath string
    Type      Type      // field type
    Tag       StructTag // field tag string
    Offset    uintptr   // offset within struct, in bytes
    Index     []int     // index sequence for Type.FieldByIndex
    Anonymous bool      // is an embedded field
}

Tag is a built-in type, providing Get , Loopup two methods to parse the value in the tag and return the value of the specified key:

func (tag StructTag) Get(key string) string
func (tag StructTag) Lookup(key string) (value string, ok bool)

Get also calls the Lookup method internally. The difference is that Lookup key exists and is in the tag through the return value Get method completely ignores this judgment.

Summarize

This article mainly introduces what Go language is, and how to use reflection to obtain the structure label. In our daily development, we use some libraries to provide good labels, and we rarely develop and use them ourselves. If you are interested, you can read validae to see how he parses tag in the structure, or you can implement a verification library yourself as a hands-on project.

The code in the article has been uploaded github : https://github.com/asong2020/Golang_Dream/tree/master/code_demo/struct_tag_demo

, this is the end of this article, my name is asong , see you in the next issue.

Welcome to pay attention to the public account: Golang DreamWorks


asong
605 声望908 粉丝