What are the use(s) for tags in Go?

前端 未结 3 1960
鱼传尺愫
鱼传尺愫 2020-11-22 01:38

In the Go Language Specification, it mentions a brief overview of tags:

A field declaration may be followed by an optional string literal tag, whic

相关标签:
3条回答
  • 2020-11-22 02:19

    It's some sort of specifications that specifies how packages treat with a field that is tagged.

    for example:

    type User struct {
        FirstName string `json:"first_name"`
        LastName string `json:"last_name"`
    }
    

    json tag informs json package that marshalled output of following user

    u := User{
            FirstName: "some first name",
            LastName:  "some last name",
        }
    

    would be like this:

    {"first_name":"some first name","last_name":"some last name"}
    

    other example is gorm package tags declares how database migrations must be done:

    type User struct {
      gorm.Model
      Name         string
      Age          sql.NullInt64
      Birthday     *time.Time
      Email        string  `gorm:"type:varchar(100);unique_index"`
      Role         string  `gorm:"size:255"` // set field size to 255
      MemberNumber *string `gorm:"unique;not null"` // set member number to unique and not null
      Num          int     `gorm:"AUTO_INCREMENT"` // set num to auto incrementable
      Address      string  `gorm:"index:addr"` // create index with name `addr` for address
      IgnoreMe     int     `gorm:"-"` // ignore this field
    }
    

    In this example for the field Email with gorm tag we declare that corresponding column in database for the field email must be of type varchar and 100 maximum length and it also must have unique index.

    other example is binding tags that are used very mostly in gin package.

    type Login struct {
        User     string `form:"user" json:"user" xml:"user"  binding:"required"`
        Password string `form:"password" json:"password" xml:"password" binding:"required"`
    }
    
    
    var json Login
    if err := c.ShouldBindJSON(&json); err != nil {
         c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
         return
    }
    

    the binding tag in this example gives hint to gin package that the data sent to API must have user and password fields cause these fields are tagged as required.

    So generraly tags are data that packages require to know how should they treat with data of type different structs and best way to get familiar with the tags a package needs is READING A PACKAGE DOCUMENTATION COMPLETELY.

    0 讨论(0)
  • 2020-11-22 02:25

    Here is a really simple example of tags being used with the encoding/json package to control how fields are interpreted during encoding and decoding:

    Try live: http://play.golang.org/p/BMeR8p1cKf

    package main
    
    import (
        "fmt"
        "encoding/json"
    )
    
    type Person struct {
        FirstName  string `json:"first_name"`
        LastName   string `json:"last_name"`
        MiddleName string `json:"middle_name,omitempty"`
    }
    
    func main() {
        json_string := `
        {
            "first_name": "John",
            "last_name": "Smith"
        }`
    
        person := new(Person)
        json.Unmarshal([]byte(json_string), person)
        fmt.Println(person)
    
        new_json, _ := json.Marshal(person)
        fmt.Printf("%s\n", new_json)
    }
    
    // *Output*
    // &{John Smith }
    // {"first_name":"John","last_name":"Smith"}
    

    The json package can look at the tags for the field and be told how to map json <=> struct field, and also extra options like whether it should ignore empty fields when serializing back to json.

    Basically, any package can use reflection on the fields to look at tag values and act on those values. There is a little more info about them in the reflect package
    http://golang.org/pkg/reflect/#StructTag :

    By convention, tag strings are a concatenation of optionally space-separated key:"value" pairs. Each key is a non-empty string consisting of non-control characters other than space (U+0020 ' '), quote (U+0022 '"'), and colon (U+003A ':'). Each value is quoted using U+0022 '"' characters and Go string literal syntax.

    0 讨论(0)
  • 2020-11-22 02:27

    A tag for a field allows you to attach meta-information to the field which can be acquired using reflection. Usually it is used to provide transformation info on how a struct field is encoded to or decoded from another format (or stored/retrieved from a database), but you can use it to store whatever meta-info you want to, either intended for another package or for your own use.

    As mentioned in the documentation of reflect.StructTag, by convention the value of a tag string is a space-separated list of key:"value" pairs, for example:

    type User struct {
        Name string `json:"name" xml:"name"`
    }
    

    The key usually denotes the package that the subsequent "value" is for, for example json keys are processed/used by the encoding/json package.

    If multiple information is to be passed in the "value", usually it is specified by separating it with a comma (','), e.g.

    Name string `json:"name,omitempty" xml:"name"`
    

    Usually a dash value ('-') for the "value" means to exclude the field from the process (e.g. in case of json it means not to marshal or unmarshal that field).

    Example of accessing your custom tags using reflection

    We can use reflection (reflect package) to access the tag values of struct fields. Basically we need to acquire the Type of our struct, and then we can query fields e.g. with Type.Field(i int) or Type.FieldByName(name string). These methods return a value of StructField which describes / represents a struct field; and StructField.Tag is a value of type StructTag which describes / represents a tag value.

    Previously we talked about "convention". This convention means that if you follow it, you may use the StructTag.Get(key string) method which parses the value of a tag and returns you the "value" of the key you specify. The convention is implemented / built into this Get() method. If you don't follow the convention, Get() will not be able to parse key:"value" pairs and find what you're looking for. That's also not a problem, but then you need to implement your own parsing logic.

    Also there is StructTag.Lookup() (was added in Go 1.7) which is "like Get() but distinguishes the tag not containing the given key from the tag associating an empty string with the given key".

    So let's see a simple example:

    type User struct {
        Name  string `mytag:"MyName"`
        Email string `mytag:"MyEmail"`
    }
    
    u := User{"Bob", "bob@mycompany.com"}
    t := reflect.TypeOf(u)
    
    for _, fieldName := range []string{"Name", "Email"} {
        field, found := t.FieldByName(fieldName)
        if !found {
            continue
        }
        fmt.Printf("\nField: User.%s\n", fieldName)
        fmt.Printf("\tWhole tag value : %q\n", field.Tag)
        fmt.Printf("\tValue of 'mytag': %q\n", field.Tag.Get("mytag"))
    }
    

    Output (try it on the Go Playground):

    Field: User.Name
        Whole tag value : "mytag:\"MyName\""
        Value of 'mytag': "MyName"
    
    Field: User.Email
        Whole tag value : "mytag:\"MyEmail\""
        Value of 'mytag': "MyEmail"
    

    GopherCon 2015 had a presentation about struct tags called:

    The Many Faces of Struct Tags (slide) (and a video)

    Here is a list of commonly used tag keys:

    • json      - used by the encoding/json package, detailed at json.Marshal()
    • xml       - used by the encoding/xml package, detailed at xml.Marshal()
    • bson      - used by gobson, detailed at bson.Marshal()
    • protobuf  - used by github.com/golang/protobuf/proto, detailed in the package doc
    • yaml      - used by the gopkg.in/yaml.v2 package, detailed at yaml.Marshal()
    • db        - used by the github.com/jmoiron/sqlx package; also used by github.com/go-gorp/gorp package
    • orm       - used by the github.com/astaxie/beego/orm package, detailed at Models – Beego ORM
    • gorm      - used by the github.com/jinzhu/gorm package, examples can be found in their doc: Models
    • valid     - used by the github.com/asaskevich/govalidator package, examples can be found in the project page
    • datastore - used by appengine/datastore (Google App Engine platform, Datastore service), detailed at Properties
    • schema    - used by github.com/gorilla/schema to fill a struct with HTML form values, detailed in the package doc
    • asn       - used by the encoding/asn1 package, detailed at asn1.Marshal() and asn1.Unmarshal()
    • csv       - used by the github.com/gocarina/gocsv package
    0 讨论(0)
提交回复
热议问题