Go 中标签的用途是啥?
Posted
技术标签:
【中文标题】Go 中标签的用途是啥?【英文标题】:What are the use(s) for tags in Go?Go 中标签的用途是什么? 【发布时间】:2012-06-07 04:43:42 【问题描述】:在Go Language Specification中,提到了标签的简要概述:
字段声明后面可以跟一个可选的字符串文字标记, 它成为相应字段中所有字段的属性 字段声明。标签通过反射可见 接口,但在其他方面被忽略。
// 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 解释,我想知道是否有人可以向我提供这些标签的用途?
【问题讨论】:
我有一个有关使用“语义”cmets 的相关问题:***.com/questions/53101458/… 更正链接应该是***.com/q/53487371/1569204 【参考方案1】:字段的标签允许您将元信息附加到可以使用反射获取的字段。通常它用于提供有关如何将结构字段编码为另一种格式或从另一种格式解码(或从数据库中存储/检索)的转换信息,但您可以使用它来存储您想要的任何元信息,或者用于另一个打包或自用。
正如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
] 6 类型的值,它描述/表示标签值。
之前我们谈到了“约定”。这个约定意味着如果你遵循它,你可以使用 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)(和一个video)
这里是常用的标签键列表:
json
- 由 encoding/json
包使用,详情参见 json.Marshal()
xml
- 由 encoding/xml
包使用,详细信息在 xml.Marshal()
bson
- 由gobson 使用,详情见bson.Marshal()
;也由mongo-go 驱动程序提供,详情请见bson package doc
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
使用,示例可以在他们的docs 中找到
valid
- 由github.com/asaskevich/govalidator
包使用,示例可以在项目页面中找到
datastore
- 由 appengine/datastore
使用(Google App Engine 平台,Datastore 服务),详细信息在 Properties
schema
- github.com/gorilla/schema
使用 html 表单值填充 struct
,在包文档中有详细说明
asn
- 由 encoding/asn1
包使用,详细信息在 asn1.Marshal()
和 asn1.Unmarshal()
csv
- 由 github.com/gocarina/gocsv
包使用
env
- 由 github.com/caarlos0/env
包使用
【讨论】:
优秀的答案。这里的信息比有十倍业力的信息有用得多。 非常好的总结! 惊人的答案,感谢您提供所有这些信息! 很好的答案我正在为mongoose Colections
使用这个非常有帮助
这一定是我见过的关于堆栈溢出的最佳答案之一!真的很感激。【参考方案2】:
这是一个非常简单的标签示例,它与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 结构字段,以及额外的选项,例如在序列化回 json 时是否应忽略空字段。
基本上,任何包都可以在字段上使用反射来查看标记值并对这些值进行操作。在反射包中有更多关于它们的信息http://golang.org/pkg/reflect/#StructTag:
按照惯例,标签字符串是可选的串联 空格分隔的键:“值”对。每个键都是一个非空字符串 由除空格以外的非控制字符 (U+0020 ' ') 组成, 引号 (U+0022 '"') 和冒号 (U+003A ':')。每个值使用 U+0022 '"' 字符和 Go 字符串文字语法。
【讨论】:
有点像 Java 注释? @isbadawi:我不是 java 人,但快速浏览一下 java 注释的定义,是的,它们似乎正在实现相同的目标;将元数据附加到可以在运行时检查的元素。 不是真正的 java 注释。 Java 注释是类型安全的,并且经过编译时检查 - 而不是像 go 这样的字符串文字。 Java 注释比 golang 基本元数据规定更强大和健壮。 作为 Go 的 MongoDB 驱动程序的一部分,mgo 在其 bson 包中也使用了标签(也可以单独使用)。它使您可以精确控制生成的 BSON。见godoc.org/labix.org/v2/mgo/bson#pkg-files 除了JSON和BSON还有其他例子吗?【参考方案3】:这是一种规范,指定包如何处理带有标签的字段。
例如:
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,并且还必须具有唯一索引。
另一个例子是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
本例中的绑定标签提示 gin 包发送到 API 的数据必须有用户和密码字段,因为这些字段是按要求标记的。
所以通常标签是包需要知道它们应该如何处理不同结构类型的数据的数据,并且熟悉包需要的标签的最佳方法是完整阅读包文档。
【讨论】:
以上是关于Go 中标签的用途是啥?的主要内容,如果未能解决你的问题,请参考以下文章