lia*_*dee 364 reflection struct go
在Go语言规范中,它提到了标签的简要概述:
字段声明后面可以跟一个可选的字符串文字标记,该标记成为相应字段声明中所有字段的属性.标签通过反射界面可见,但否则将被忽略.
Run Code Online (Sandbox Code Playgroud)// 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,我想知道是否有人可以提供我这些标签的用途?
icz*_*cza 573
字段的标记允许您将元信息附加到可以使用反射获取的字段.通常它用于提供关于如何将结构字段编码到另一种格式或从另一种格式解码(或从数据库存储/检索)的转换信息,但是您可以使用它来存储您想要的任何元信息,或者用于另一个包装或供您自己使用.
正如文档中所提到的reflect.StructTag,按照惯例,标记字符串的值是以空格分隔的key:"value"对列表,例如:
type User struct {
Name string `json:"name" xml:"name"`
}
Run Code Online (Sandbox Code Playgroud)
的key通常表示包,随后的"value"是,例如json密钥被处理/使用的encoding/json包.
如果要在其中传递多个信息"value",通常通过用逗号(',')分隔它来指定,例如
Name string `json:"name,omitempty" xml:"name"`
Run Code Online (Sandbox Code Playgroud)
通常是一个破折号值('-'),用于"value"从过程中排除字段的方法(例如,如果json它意味着不对该字段进行编组或解组).
我们可以使用反射(reflect包)来访问struct字段的标记值.基本上我们需要获取Type我们的结构,然后我们可以查询字段,例如用Type.Field(i int)或Type.FieldByName(name string).这些方法返回一个StructField描述/表示结构字段的值; 并且StructField.Tag是StructTag描述/表示标记值的类型的值.
以前我们谈过"会议".这种约定意味着,如果你遵循它,你可以使用StructTag.Get(key string)它解析变量的值,并返回该方法"value"的key指定.该约定在此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"))
}
Run Code Online (Sandbox Code Playgroud)
输出(在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"
Run Code Online (Sandbox Code Playgroud)
GopherCon 2015有一个关于struct标签的演示文稿:
json - encoding/json包装使用,详情请参阅json.Marshal()xml - encoding/xml包装使用,详情请参阅xml.Marshal()bson - 由gobson使用,详情请参阅bson.Marshal()protobuf - 使用者github.com/golang/protobuf/proto,详见包docyaml - gopkg.in/yaml.v2包装使用,详情请参阅yaml.Marshal()db - github.com/jmoiron/sqlx包装使用; 也用于github.com/go-gorp/gorp包装orm - github.com/astaxie/beego/orm包装使用,详见Models - Beego ORMgorm - github.com/jinzhu/gorm包使用的例子可以在他们的doc:Models中找到valid - 由github.com/asaskevich/govalidator包使用,示例可以在项目页面中找到datastore- 由appengine/datastore(Google App Engine平台,数据存储区服务)使用,详见属性schema - 用于github.com/gorilla/schema填充structHTML表单值,详见包docasn - encoding/asn1包装使用,详细说明asn1.Marshal()和asn1.Unmarshal()csv - 由github.com/gocarina/gocsv包使用jdi*_*jdi 156
以下是与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"}
Run Code Online (Sandbox Code Playgroud)
JSON的包可以看一下字段的标签和被告知如何JSON <=>结构领域,而像序列化回JSON时,它是否应忽略空白领域还额外的选项映射.
基本上,任何包都可以使用字段上的反射来查看标记值并对这些值进行操作.在反射包
http://golang.org/pkg/reflect/#StructTag中有更多关于它们的信息:
按照惯例,标记字符串是可选的空格分隔的键:"值"对的串联.每个键都是一个非空字符串,由空格(U + 0020''),引号(U + 0022'"')和冒号(U + 003A':')以外的非控制字符组成.每个值都被引用使用U + 0022'"'字符和Go字符串文字语法.
它是某种规范,指定包如何处理标记的字段。
例如:
type User struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
}
Run Code Online (Sandbox Code Playgroud)
json 标签通知json包编组以下用户的输出
u := User{
FirstName: "some first name",
LastName: "some last name",
}
Run Code Online (Sandbox Code Playgroud)
会是这样的:
{"first_name":"some first name","last_name":"some last name"}
Run Code Online (Sandbox Code Playgroud)
另一个例子是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
}
Run Code Online (Sandbox Code Playgroud)
在此示例中,对于带有 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
}
Run Code Online (Sandbox Code Playgroud)
此示例中的绑定标记向 gin 包提示发送到 API 的数据必须具有用户和密码字段,因为这些字段根据需要进行标记。
因此,通常标签是包需要知道如何处理不同结构类型的数据的数据,熟悉包所需标签的最佳方法是完整阅读包文档。