英文:
What are the use(s) for struct tags in Go?
问题
在Go语言规范中,它提到了标签的简要概述:
字段声明后可以跟一个可选的字符串字面标签,该标签将成为相应字段声明中所有字段的属性。标签可以通过反射接口进行访问,但在其他情况下会被忽略。
// 与TimeStamp协议缓冲区对应的结构体。 // 标签字符串定义了协议缓冲区字段的编号。 struct { microsec uint64 "field 1" serverIP6 uint64 "field 2" process string "field 3" }
在我看来,这是一个非常简短的解释,我想知道这些标签有什么用途?
英文:
In the Go Language Specification, it mentions a brief overview of tags:
> 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.
>
> // 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"
> }
This is a very short explanation IMO, and I was wondering if anyone could provide me with what use these tags would be?
答案1
得分: 993
字段的标签允许您将元信息附加到字段上,可以使用反射来获取这些元信息。通常,它用于提供有关如何将结构字段编码到另一种格式(或从数据库中存储/检索)的转换信息,但您可以使用它来存储任何您想要的元信息,无论是为另一个包还是为您自己使用。
如reflect.StructTag
的文档中所述,按照约定,标签字符串的值是一个由空格分隔的key:"value"
对的列表,例如:
type User struct {
Name string `json:"name" xml:"name"`
}
key
通常表示后续的"value"
所属的包,例如json
键由encoding/json
包处理/使用。
如果要传递多个信息到"value"
中,通常使用逗号(','
)进行分隔,例如:
Name string `json:"name,omitempty" xml:"name"`
通常,"value"
的破折号值('-'
)表示将字段从处理过程中排除(例如,在json
中,它表示不对该字段进行编组或解组)。
使用反射访问自定义标签的示例
我们可以使用反射(reflect
包)来访问结构字段的标签值。基本上,我们需要获取我们结构的Type
,然后我们可以使用Type.Field(i int)
或Type.FieldByName(name string)
查询字段。这些方法返回一个StructField
的值,它描述/表示一个结构字段;而StructField.Tag
是一个StructTag
类型的值,它描述/表示一个标签值。
之前我们谈到了“约定”。这个约定意味着如果您遵循它,您可以使用StructTag.Get(key string)
方法解析标签的值,并返回您指定的key
的"value"
。这个“约定”被实现/内置到了这个Get()
方法中。如果您不遵循这个约定,Get()
将无法解析key:"value"
对并找到您要查找的内容。这也不是问题,但是您需要实现自己的解析逻辑。
还有StructTag.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有一个关于结构标签的演讲:
The Many Faces of Struct Tags (slide)(以及视频)
这是一些常用的标签键列表:
-
json
- 由encoding/json
包使用,详细信息请参阅json.Marshal()
-
xml
- 由encoding/xml
包使用,详细信息请参阅xml.Marshal()
-
bson
- 由gobson使用,详细信息请参阅bson.Marshal()
;也由mongo-go驱动程序使用,详细信息请参阅bson包文档 -
protobuf
- 由github.com/golang/protobuf/proto
使用,详细信息请参阅该包文档 -
yaml
- 由gopkg.in/yaml.v2
包使用,详细信息请参阅yaml.Marshal()
-
db
- 由github.com/jmoiron/sqlx
包使用;也由github.com/go-gorp/gorp
包使用 -
orm
- 由github.com/astaxie/beego/orm
包使用,详细信息请参阅Models – Beego ORM -
gorm
- 由gorm.io/gorm
使用,示例可以在它们的文档中找到 -
valid
- 由github.com/asaskevich/govalidator
包使用,示例可以在项目页面中找到 -
datastore
- 由appengine/datastore
(Google App Engine平台,Datastore服务)使用,详细信息请参阅Properties -
schema
- 由github.com/gorilla/schema
使用,用于填充struct
与HTML表单值,详细信息请参阅该包文档 -
asn
- 由encoding/asn1
包使用,详细信息请参阅asn1.Marshal()
和asn1.Unmarshal()
-
csv
- 由github.com/gocarina/gocsv
包使用 -
env
- 由github.com/caarlos0/env
包使用
英文:
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 theencoding/json
package, detailed atjson.Marshal()
-
xml      
- used by theencoding/xml
package, detailed atxml.Marshal()
-
bson     
- used by gobson, detailed atbson.Marshal()
; also by the mongo-go driver, detailed at bson package doc -
protobuf 
- used bygithub.com/golang/protobuf/proto
, detailed in the package doc -
yaml     
- used by thegopkg.in/yaml.v2
package, detailed atyaml.Marshal()
-
db       
- used by thegithub.com/jmoiron/sqlx
package; also used bygithub.com/go-gorp/gorp
package -
orm      
- used by thegithub.com/astaxie/beego/orm
package, detailed at Models – Beego ORM -
gorm     
- used bygorm.io/gorm
, examples can be found in their docs -
valid    
- used by thegithub.com/asaskevich/govalidator
package, examples can be found in the project page -
datastore
- used byappengine/datastore
(Google App Engine platform, Datastore service), detailed at Properties -
schema   
- used bygithub.com/gorilla/schema
to fill astruct
with HTML form values, detailed in the package doc -
asn      
- used by theencoding/asn1
package, detailed atasn1.Marshal()
andasn1.Unmarshal()
-
csv      
- used by thegithub.com/gocarina/gocsv
package -
env
- used by thegithub.com/caarlos0/env
package
答案2
得分: 167
这是一个非常简单的示例,演示了如何使用encoding/json
包中的标签来控制在编码和解码过程中如何解释字段:
试一试:http://play.golang.org/p/BMeR8p1cKf
<!-- language: go -->
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 <=> 结构字段,还可以设置其他选项,比如在将字段序列化回json时是否忽略空字段。
基本上,任何包都可以使用反射来查看标签值并根据这些值进行操作。在reflect包中有更多关于标签的信息:
http://golang.org/pkg/reflect/#StructTag:
按照约定,标签字符串是可选的以空格分隔的key:"value"对的串联。每个key是一个非空字符串,由非控制字符组成,除了空格(U+0020 ' ')、引号(U+0022 '"')和冒号(U+003A ':')。每个value使用U+0022 '"'字符引用,并使用Go字符串字面量语法。
英文:
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
<!-- language: go -->
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.
答案3
得分: 4
这是一些规范,用于指定如何处理带有标签的字段的包。
例如:
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"` // 将字段大小设置为255
MemberNumber *string `gorm:"unique;not null"` // 将成员编号设置为唯一且非空
Num int `gorm:"AUTO_INCREMENT"` // 将num设置为自增
Address string `gorm:"index:addr"` // 为地址创建名为`addr`的索引
IgnoreMe int `gorm:"-"` // 忽略此字段
}
在这个例子中,对于带有gorm标签的Email
字段,我们声明数据库中对应的列必须是varchar类型,最大长度为100,并且必须具有唯一索引。
另一个例子是binding
标签,它在gin
包中被广泛使用。
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
}
在这个例子中,binding标签提示gin包,发送到API的数据必须具有用户和密码字段,因为这些字段被标记为必需的。
总的来说,标签是包需要的数据,用于了解它们如何处理不同结构类型的数据,最好的方法是完全阅读包的文档。
英文:
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.
答案4
得分: 0
在Go语言中,标签(tags)实际上是与结构体字段关联的元数据。它们被定义为出现在结构体定义中字段名后面的字符串字面量,并用反引号或双引号括起来。
标签在Go语言中有几个用途:
- 序列化和反序列化:标签最常见的用途之一是帮助进行数据的序列化和反序列化。例如,在将结构体编码为JSON时,编码器将使用标签值来确定用于每个字段的JSON键名。
- 反射:Go语言的反射包允许程序在运行时检查和修改程序的结构。标签可以用于提供关于结构体字段的附加信息,这些信息可以通过反射访问。
- 验证:标签可以用于验证存储在结构体中的数据。例如,一个标签可以指示特定字段必须是有效的电子邮件地址,或者必须是非负整数。
- 文档:标签可以用于提供关于结构体字段的附加文档。这对于像godoc这样的工具非常有用,它可以根据源代码注释和其他元数据为Go程序生成文档。
英文:
In Go, tags are essentially metadata associated with a struct field. They are defined as string literals that appear after the field name in a struct definition, and are enclosed in backticks or double quotes.
Tags serve several purposes in Go:
- Serialization and Deserialization: One of the most common uses of tags is to aid in the serialization and deserialization of data. For example, when encoding a struct as JSON, the encoder will use the tag values to determine the JSON key names to use for each field.
- Reflection: Go's reflection package allows programs to inspect and modify the structure of a program at runtime. Tags can be used to provide additional information about the fields of a struct that can be accessed via reflection.
- Validation: Tags can be used to validate the data stored in a struct. For example, a tag could indicate that a particular field must be a valid email address, or that it must be a non-negative integer.
- Documentation: Tags can be used to provide additional documentation about the fields of a struct. This can be useful for tools like godoc, which generate documentation for Go programs based on source code comments and other metadata.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论