ChatGPT解决这个技术问题 Extra ChatGPT

Go 中结构标签的用途是什么?

Go Language Specification 中,它提到了标签的简要概述:

字段声明后面可以跟一个可选的字符串文字标记,它成为相应字段声明中所有字段的属性。标签通过反射界面可见,但在其他方面被忽略。 // 一个对应于 TimeStamp 协议缓冲区的结构体。 // 标签字符串定义协议缓冲区字段编号。 struct { microsec uint64 “field 1” serverIP6 uint64 “field 2” 进程字符串 “field 3” }

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

我有一个有关使用“语义”评论的相关问题:stackoverflow.com/questions/53101458/…

i
icza

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

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/ataxie/beego/orm 包使用,详见 Models – Beego ORM

gorm - 由 gorm.io/gorm 使用,示例可以在他们的文档中找到

有效 - 由 github.com/asaskevich/govalidator 包使用,示例可以在项目页面中找到

datastore - 由 appengine/datastore(Google App Engine 平台,Datastore 服务)使用,详情见属性

schema - 被 github.com/gorilla/schema 用来用 HTML 表单值填充结构,详细信息在包文档中

asn - 由 encoding/asn1 包使用,详见 asn1.Marshal() 和 asn1.Unmarshal()

csv - 被 github.com/gocarina/gocsv 包使用

env - 由 github.com/caarlos0/env 包使用


优秀的答案。这里的信息比拥有十倍业力的信息要有用得多。
非常好的总结!
惊人的答案,感谢您提供所有这些信息!
很好的答案我用这个mongoose Colections非常有帮助
这一定是我见过的关于堆栈溢出的最佳答案之一!非常感谢。
S
Siu Ching Pong -Asuka Kenji-

以下是与 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,还有其他例子吗?
M
Milad Khodabandehloo

它是某种规范,指定包如何处理带有标签的字段。

例如:

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,并且它还必须具有唯一索引。

另一个示例是 gin 包中最常使用的 binding 标记。

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 的数据必须具有用户和密码字段,因为这些字段是按要求标记的。

所以一般标签是包需要知道他们应该如何处理不同结构类型的数据的数据,熟悉包需要的标签的最佳方法是完整地阅读包文档。