原文链接:为什么 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 名称。
例如,对于序列化和反序列化,能够应用 json
、xml
、yaml
等;对于数据库操作,能够应用 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.Field
或 Type.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)