关于反射:Go中标签的用途是什么?

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

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

A field declaration may be followed by an optional string literal tag,
which becomes an attribute for all the fields in the corresponding
field declaration. The tags are made visible through a reflection
interface but are otherwise ignored.

1
2
3
4
5
6
7
// A struct corresponding to the TimeStamp protocol buffer.
// The tag strings define the protocol buffer field numbers.
struct {
  microsec  uint64"field 1"
  serverIP6 uint64"field 2"
  process   string"field 3"
}

这是IMO的简短说明,我想知道是否有人可以为我提供这些标签的用途?


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

reflect.StructTag文档中所述,按照惯例,标记字符串的值是由key:"value"对组成的空格分隔列表,例如:

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

key通常表示后续"value"用于的包,例如json键由encoding/json包处理/使用。

如果要在"value"中传递多个信息,通常通过用逗号(',')隔开来指定它,例如

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

通常,"value"的破折号('-')表示将字段从进程中排除(例如,在json的情况下,意味着不封送或取消封送该字段)。

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

我们可以使用反射(reflect包)来访问结构字段的标记值。基本上我们需要获取结构的Type,然后我们可以查询字段,例如使用Type.Field(i int)Type.FieldByName(name string)。这些方法返回的值StructField描述/表示一个struct字段; StructField.Tag是类型为StructTag的值,该值描述/表示标签值。

先前我们谈论过"公约"。此约定意味着,如果遵循该约定,则可以使用StructTag.Get(key string)方法,该方法将解析标记的值并返回指定的key"value"。约定已在Get()方法中实现/内置。如果您不遵循约定,则Get()将无法解析key:"value"对并找到您要查找的内容。这也不是问题,但是您需要实现自己的解析逻辑。

还有StructTag.Lookup()(在Go 1.7中添加了),它类似于Get(),但是将不包含给定键的标签与将空字符串与给定键相关联的标签区分开来。

因此,让我们看一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type User struct {
    Name  string `mytag:"MyName"`
    Email string `mytag:"MyEmail"`
}

u := User{"Bob","[email protected]"}
t := reflect.TypeOf(u)

for _, fieldName := range []string{"Name","Email"} {
    field, found := t.FieldByName(fieldName)
    if !found {
        continue
    }
    fmt.Printf("
Field: User.%s
", fieldName)
    fmt.Printf("\tWhole tag value : %q
", field.Tag)
    fmt.Printf("\tValue of 'mytag': %q
", field.Tag.Get("mytag"))
}

输出(在Go Playground上尝试):

1
2
3
4
5
6
7
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标签的演示,名为:

结构标签的许多面孔(幻灯片)(和视频)

以下是常用标签键的列表:

  • json-由encoding/json包使用,在json.Marshal()中有详细说明
  • xml-由encoding/xml软件包使用,详细信息在xml.Marshal()
  • bson-由高布森使用,详细说明在bson.Marshal()
  • protobuf-由github.com/golang/protobuf/proto使用,在软件包doc中有详细说明
  • yaml-由gopkg.in/yaml.v2包使用,在yaml.Marshal()中有详细说明
  • db-由github.com/jmoiron/sqlx软件包使用;也被github.com/go-gorp/gorp包使用
  • orm-由github.com/astaxie/beego/orm包使用,在"模型– Beego ORM"中有详细说明
  • gorm-由github.com/jinzhu/gorm包使用,示例可以在其文档中找到:模型
  • valid-由github.com/asaskevich/govalidator包使用,示例可以在项目页面中找到
  • datastore-由appengine/datastore(Google App Engine平台,数据存储服务)使用,在"属性"中有详细说明
  • schema-由github.com/gorilla/schema用于用HTML表单值填充struct,详细信息在软件包doc中
  • asn-由encoding/asn1软件包使用,在asn1.Marshal()asn1.Unmarshal()中有详细说明
  • csv-由github.com/gocarina/gocsv包使用


这是一个非常简单的示例,该标签与encoding/json包一起使用以控制在编码和解码期间如何解释字段:

尝试直播:http://play.golang.org/p/BMeR8p1cKf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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
", new_json)
}

// *Output*
// &{John Smith }
// {"first_name":"John","last_name":"Smith"}

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

基本上,任何包都可以在字段上使用反射来查看标记值并对其执行操作。在反射包中有一些关于它们的更多信息
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.