关于go:为什么-Go-语言-struct-要使用-tags

43次阅读

共计 4205 个字符,预计需要花费 11 分钟才能阅读完成。

原文链接:为什么 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 main

import (
    "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 的库的列表,我复制在上面了,感兴趣的同学能够看看源码,再持续深刻学习。

Tag Documentation
xml https://pkg.go.dev/encoding/xml
json https://pkg.go.dev/encoding/json
asn1 https://pkg.go.dev/encoding/asn1
reform https://pkg.go.dev/gopkg.in/reform.v1
dynamodb https://docs.aws.amazon.com/sdk-for-go/api/service/dynamodb/d…
bigquery https://pkg.go.dev/cloud.google.com/go/bigquery
datastore https://pkg.go.dev/cloud.google.com/go/datastore
spanner https://pkg.go.dev/cloud.google.com/go/spanner
bson https://pkg.go.dev/labix.org/v2/mgo/bson, https://pkg.go.dev/go.mongodb.org/mongo-driver/bson/bsoncodec
gorm https://pkg.go.dev/github.com/jinzhu/gorm
yaml https://pkg.go.dev/gopkg.in/yaml.v2
toml https://pkg.go.dev/github.com/pelletier/go-toml
validate https://github.com/go-playground/validator
mapstructure https://pkg.go.dev/github.com/mitchellh/mapstructure
parser https://pkg.go.dev/github.com/alecthomas/participle
protobuf https://github.com/golang/protobuf
db https://github.com/jmoiron/sqlx
url https://github.com/google/go-querystring
feature https://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)

正文完
 0