原文链接:为什么 Go 语言 struct 要应用 tags

在 Go 语言中,struct 是一种常见的数据类型,它能够用来示意简单的数据结构。在 struct 中,咱们能够定义多个字段,每个字段能够有不同的类型和名称。

除了这些根本信息之外,Go 还提供了 struct tags,它能够用来指定 struct 中每个字段的元信息。

在本文中,咱们将探讨为什么 Go 语言中须要应用 struct tags,以及 struct tags 的应用场景和劣势。

struct tags 的应用

struct tags 应用还是很宽泛的,特地是在 json 序列化,或者是数据库 ORM 映射方面。

在定义上,它以 key:value 的模式呈现,跟在 struct 字段前面,除此之外,还有以下几点须要留神:

应用反引号

在申明 struct tag 时,应用反引号 ` 突围 tag 的值,能够避免转义字符的影响,使 tag 更容易读取和了解。例如:

type User struct {    ID    int    `json:"id" db:"id"`    Name  string `json:"name" db:"name"`    Email string `json:"email" db:"email"`}

防止应用空格

在 struct tag 中,应该防止应用空格,特地是在 tag 名称和 tag 值之间。应用空格可能会导致编码或解码谬误,并使代码更难以保护。例如:

// 不标准的写法type User struct {    ID    int    `json: "id" db: "id"`    Name  string `json: "name" db: "name"`    Email string `json: "email" db: "email"`}// 标准的写法type User struct {    ID    int    `json:"id" db:"id"`    Name  string `json:"name" db:"name"`    Email string `json:"email" db:"email"`}

防止反复

在 struct 中,应该防止重复使用同一个 tag 名称。如果重复使用同一个 tag 名称,编译器可能会无奈辨认 tag,从而导致编码或解码谬误。例如:

// 不标准的写法type User struct {    ID    int    `json:"id" db:"id"`    Name  string `json:"name" db:"name"`    Email string `json:"email" db:"name"`}// 标准的写法type User struct {    ID    int    `json:"id" db:"id"`    Name  string `json:"name" db:"name"`    Email string `json:"email" db:"email"`}

应用标准化的 tag 名称

为了使 struct tag 更加标准化和易于保护,应该应用一些标准化的 tag 名称。

例如,对于序列化和反序列化,能够应用 jsonxmlyaml 等;对于数据库操作,能够应用 db

type User struct {    ID       int    `json:"id" db:"id"`    Name     string `json:"name" db:"name"`    Password string `json:"-" db:"password"` // 疏忽该字段    Email    string `json:"email" db:"email"`}

其中,Password 字段前面的 - 示意疏忽该字段,也就是说该字段不会被序列化或反序列化。

多个 tag 值

如果一个字段须要指定多个 tag 值,能够应用 , 将多个 tag 值分隔开。例如:

type User struct {    ID        int    `json:"id" db:"id"`    Name      string `json:"name" db:"name"`    Email     string `json:"email,omitempty" db:"email,omitempty"`}

其中 omitempty 示意如果该字段值为空,则不序列化该字段。

struct tags 的原理

Go 的反射库提供了一些办法,能够让咱们在程序运行时获取和解析构造体标签。

介绍这些办法之前,先来看看 reflect.StructField ,它是形容构造体字段的数据类型。定义如下:

type StructField struct {    Name      string      // 字段名    Type      Type        // 字段类型    Tag       StructTag   // 字段标签}

构造体中还有一些其余字段,被我省略了,只保留了和本文相干的。

在构造体的反射中,咱们常常应用 reflect.TypeOf 获取类型信息,而后应用 Type.FieldType.FieldByName() 获取构造体字段的 reflect.StructField,而后依据 StructField 中的信息做进一步解决。

例如,能够通过 StructField.Tag.Get 办法获取构造体字段的标签值。

上面看一段代码:

package mainimport (    "fmt"    "reflect")type User struct {    Name string `json:"name"`    Age  int    `json:"age"`}type Manager struct {    Title string `json:"title"`    User}func main() {    m := Manager{Title: "Manager", User: User{Name: "Alice", Age: 25}}    mt := reflect.TypeOf(m)    // 获取 User 字段的 reflect.StructField    userField, _ := mt.FieldByName("User")    fmt.Println("Field 'User' exists:", userField.Name, userField.Type)    // 获取 User.Name 字段的 reflect.StructField    nameField, _ := userField.Type.FieldByName("Name")    tag := nameField.Tag.Get("json")    fmt.Println("User.Name tag:", tag)}

运行以上代码,输入后果如下:

Field 'User' exists: User {string int}User.Name tag: "name"

struct tags 的劣势

应用 struct tag 的次要劣势之一是能够在运行时通过反射来拜访和操作 struct 中的字段

比方在 Go Web 开发中,经常须要将 HTTP 申请中的参数绑定到一个 struct 中。这时,咱们能够应用 struct tag 指定每个字段对应的参数名称、验证规定等信息。在接管到 HTTP 申请时,就能够应用反射机制读取这些信息,并依据信息来验证参数是否非法。

另外,在将 struct 序列化为 JSON 或者其余格局时,咱们也能够应用 struct tag 来指定每个字段在序列化时的名称和规定。

此外,应用 struct tag 还能够进步代码的可读性可维护性。在一个大型的我的项目中,struct 中的字段通常会蕴含很多不同的元信息,比方数据库中的表名、字段名、索引、验证规定等等。

如果没有 struct tag,咱们可能须要将这些元信息放在正文中或者在代码中进行硬编码。这样会让代码变得难以保护和批改。而应用 struct tag 能够将这些元信息与 struct 字段严密关联起来,使代码更加清晰和易于保护。

罕用的 struct tags

在 Go 的官网 wiki 中,有一个罕用的 struct tags 的库的列表,我复制在上面了,感兴趣的同学能够看看源码,再持续深刻学习。

TagDocumentation
xmlhttps://pkg.go.dev/encoding/xml
jsonhttps://pkg.go.dev/encoding/json
asn1https://pkg.go.dev/encoding/asn1
reformhttps://pkg.go.dev/gopkg.in/reform.v1
dynamodbhttps://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/d...
bigqueryhttps://pkg.go.dev/cloud.google.com/go/bigquery
datastorehttps://pkg.go.dev/cloud.google.com/go/datastore
spannerhttps://pkg.go.dev/cloud.google.com/go/spanner
bsonhttps://pkg.go.dev/labix.org/v2/mgo/bson, https://pkg.go.dev/go.mongodb.org/mongo-driver/bson/bsoncodec
gormhttps://pkg.go.dev/github.com/jinzhu/gorm
yamlhttps://pkg.go.dev/gopkg.in/yaml.v2
tomlhttps://pkg.go.dev/github.com/pelletier/go-toml
validatehttps://github.com/go-playground/validator
mapstructurehttps://pkg.go.dev/github.com/mitchellh/mapstructure
parserhttps://pkg.go.dev/github.com/alecthomas/participle
protobufhttps://github.com/golang/protobuf
dbhttps://github.com/jmoiron/sqlx
urlhttps://github.com/google/go-querystring
featurehttps://github.com/nikolaydubina/go-featureprocessing

以上就是本文的全部内容,如果感觉还不错的话欢送点赞转发关注,感激反对。


参考文章:

  • https://github.com/golang/go/wiki/Well-known-struct-tags

举荐浏览:

  • [为什么 Go 不反对 []T 转换为 []interface](https://mp.weixin.qq.com/s/cwDEgnicK4jkuNpzulU2bw)