在Go语言规范中,它提到了标签的简要概述:

字段声明后面可以跟着一个可选的字符串字面值标签, 中所有字段的属性 字段声明。标签通过反射可见 接口,否则将被忽略。 //一个对应于TimeStamp协议缓冲区的结构。 //标签字符串定义协议缓冲区字段号。 结构体{ Microsec uint64 "field 1" serverIP6 uint64 "field 2" 进程字符串"field 3" }

这是一个非常简短的解释,我想知道是否有人可以提供给我这些标签有什么用?


当前回答

下面是一个非常简单的例子,在encoding/json包中使用标签来控制在编码和解码期间如何解释字段:

尝试直播: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"}

json包可以查看字段的标签,并被告知如何映射json <=> struct field,以及额外的选项,如在序列化回json时是否应该忽略空字段。

基本上,任何包都可以在字段上使用反射来查看标记值并对这些值进行操作。在reflect包中有更多关于它们的信息 http://golang.org/pkg/reflect/ StructTag:

按照惯例,标记字符串是可选的连接 空格分隔键:“value”对。每个键都是非空字符串 由空格以外的非控制字符(U+0020 ' ')组成, 引号(U+0022 ' ' '),冒号(U+003A ':')。每个值都引用使用 U+0022 '"'字符和Go字符串字面量语法。

其他回答

下面是一个非常简单的例子,在encoding/json包中使用标签来控制在编码和解码期间如何解释字段:

尝试直播: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"}

json包可以查看字段的标签,并被告知如何映射json <=> struct field,以及额外的选项,如在序列化回json时是否应该忽略空字段。

基本上,任何包都可以在字段上使用反射来查看标记值并对这些值进行操作。在reflect包中有更多关于它们的信息 http://golang.org/pkg/reflect/ StructTag:

按照惯例,标记字符串是可选的连接 空格分隔键:“value”对。每个键都是非空字符串 由空格以外的非控制字符(U+0020 ' ')组成, 引号(U+0022 ' ' '),冒号(U+003A ':')。每个值都引用使用 U+0022 '"'字符和Go字符串字面量语法。

字段的标记允许您将元信息附加到可以使用反射获取的字段。通常,它用于提供关于如何将结构字段编码为或从另一种格式(或存储/从数据库检索)解码的转换信息,但您可以使用它存储您想存储的任何元信息,可以用于另一个包,也可以用于您自己的使用。

正如在反映的文件中提到的。StructTag,按照惯例,标签字符串的值是一个空格分隔的键:"value"对列表,例如:

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

键通常表示后续“值”对应的包,例如json键由encoding/json包处理/使用。

如果要在“值”中传递多个信息,通常用逗号(',')分隔。

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

通常,“value”的破折号('-')意味着将该字段从进程中排除(例如,对于json,它意味着不编组或反编组该字段)。

使用反射访问自定义标记的示例

我们可以使用反射(反射包)来访问struct字段的标签值。基本上,我们需要获取结构的类型,然后我们可以查询字段,例如用类型。字段(i int)或类型。FieldByName(名称字符串)。这些方法返回一个StructField值,它描述/表示一个struct字段;和StructField。标签是一个StructTag类型的值,它描述/表示一个标签值。

之前我们讨论了“convention”。这个约定意味着如果遵循它,就可以使用StructTag。Get(键字符串)方法,该方法解析标记的值并返回您指定的键的“值”。该约定被实现/内置到这个Get()方法中。如果您不遵循约定,Get()将无法解析key:“value”对并找到您要查找的内容。这也不是问题,但是您需要实现自己的解析逻辑。

还有structag . lookup()(在Go 1.7中添加),它“类似Get(),但将不包含给定键的标记与将空字符串与给定键关联的标记区分开来”。

让我们看一个简单的例子:

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"))
}

输出(在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有一个关于struct标签的演讲叫做:

Struct标签的许多面(幻灯片)(和一个视频)

下面是一些常用的标签键:

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(); also by the mongo-go driver, detailed at bson package doc 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 gorm.io/gorm, examples can be found in their docs 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 env - used by the github.com/caarlos0/env package

它是一种规范,指定包如何处理带标记的字段。

例如:

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

Json标签通知Json包编组以下用户的输出

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

会是这样的:

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

另一个例子是gorm包标签声明数据库迁移必须如何完成:

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
}

在本例中,对于带gorm标记的字段Email,我们声明在数据库中对应的字段Email的列必须为varchar类型,最大长度为100,并且它还必须有唯一的索引。

另一个例子是在金包中使用非常多的绑定标签。

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
}

本例中的绑定标记向gin包提示,发送到API的数据必须具有用户和密码字段,因为这些字段是根据需要标记的。

因此,一般来说,标签是包需要知道如何处理不同结构类型的数据的数据,熟悉包需要的标签的最好方法是完全阅读包文档。