盤點一下結構體標籤在 Go 中的應用
掌握了Go
語言的朋友們應該都知道,在Go
的結構體類型聲明裏面,字段聲明後可以跟一個可選的字符串標籤。
type User struct {
Name string `json:"name"`
}
上面是一個標準的例子,Name
字段聲明中指定了標籤json:"name" xml:"name"
,這個標籤值看着有點類似Java
程序裏給類屬性加的註解。
那麼這些結構體標籤有什麼用途呢,我們隨便寫管用嗎?我們平時工作中常用的結構體標籤有哪些呢?我們能不能自己定義結構體標籤?今天就帶大家掰扯清楚這些問題!
結構體標籤
Go 語言允許我們通過結構體字段標籤給一個字段附加可以被反射獲取的” 元信息 “,正好我們上篇文章實戰演示 Go 反射的使用方法和應用場景中講了 Go 語言反射使用方法相關的內容,對反射不清楚的可以先去再複習一下。
通常情況下,結構體標籤被用於提供結構體字段如何被編碼爲或者解碼自另外一種格式的轉換信息(或者是以何種形式被保存至 / 獲取自數據庫)。不過,你也可以用它存儲任何你想要設置的” 元信息 “,供其他包或者自己使用。
使用規範
結構體標籤在使用上通常是遵守下面三個規範。
結構體標籤字符串的值是一個由空格分隔的 key:"value" 對列表,例如:
type User struct {
Name string `json:"name" xml:"name"`
}
鍵,通常表示後面跟的 “值” 是被哪個包使用的,例如json
這個鍵會被encoding/json
包處理使用。如果要在 “鍵” 對應的 “值” 中傳遞多個信息,通常通過用逗號(',')分隔來指定,例如
Name string `json:"name,omitempty"`
按照慣例,如果一個字段的結構體標籤裏某個鍵的 “值” 被設置成了的破折號 ('-'),那麼就意味着告訴處理該結構體標籤鍵值的進程排除該字段。例如,把一個字段的標籤設置成下面這樣
Name string `json:"-"`
就以爲進行 JSON 編碼 / 解碼時忽略Name
這個字段。
怎麼獲取到結構體標籤
從一開始我們就說結構體標籤是給反射準備的,那麼怎麼在Go
程序裏用反射獲得到字段的結構體標籤呢?看了我們上一篇文章的同學,應該會知道,結構體字段類型相關的信息,在反射的世界裏使用reflect.StructFiled
這個類型表示的。
type StructField struct {
Name string
Type Type // field type
Tag StructTag // field tag string
......
}
如上所示,其中包含的Tag
字段即代表了字段聲明中的結構體標籤信息。讓我們通過自定義結構體標籤的例子來演示一下怎麼使用它在反射裏讀取到標籤裏的信息。
用反射獲取到自定義的結構體標籤
使用反射reflect
包訪問結構體字段的標籤值,我們需要先獲取到結構體的類型信息Type
,然後使用Type.Field(i int)
或 Type.FieldByName(name string)
,方法查詢字段信息,這兩個方法都會返回一個StructField
類型的值,上面我們也說了它在反射的世界裏用於描述一個結構體字段;而StructField.Tag
是一個StructTag
類型的值,它描述了字段的標籤。
上面我們談到了結構體標籤的使用規範,如果遵循規範給字段設置了標籤後,就可以使用StructTag
的Get
方法解析標籤的值並返回你指定的鍵的 “值”。
func (tag StructTag) Get(key string) string
爲了方便判斷一個給定的key
是否存在與標籤中,StructTag
還提供了一個Lookup
方法
func (tag StructTag) Lookup(key string) (value string, ok bool)
跟Get
方法不同的是,Lookup
會通過返回的ok
值告知給定key
是否存在與標籤中。
下面通過一個例子,演示下獲取我們自定義標籤的過程。
package main
import (
"fmt"
"reflect"
)
type User struct {
Name string `mytag:"MyName"`
Email string `mytag:"MyEmail"`
}
func main() {
u := User{"Bob", "bob@mycompany.com"}
t := reflect.TypeOf(u)
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
fmt.Printf("Field: User.%s\n", field.Name)
fmt.Printf("\tWhole tag value : %s\n", field.Tag)
fmt.Printf("\tValue of 'mytag': %s\n", field.Tag.Get("mytag"))
}
}
上面的程序會輸出
Field: User.Name
Whole tag value : mytag:"MyName"
Value of 'mytag': MyName
Field: User.Email
Whole tag value : mytag:"MyEmail"
Value of 'mytag': MyEmail
常用的結構體標籤鍵
常用的結構體標籤 Key,指的是那些被一些常用的開源包聲明使用的結構體標籤鍵。在這裏總結了一些,都是一些我們平時會用到的包,它們是:
-
json
: 由encoding/json
包使用,詳見json.Marshal()
的使用方法和實現邏輯。 -
xml
: 由encoding/xml
包使用,詳見xml.Marshal()
。 -
bson
: 由gobson
包,和mongo-go
包使用。 -
protobuf
: 由github.com/golang/protobuf/proto
使用,在包文檔中有詳細說明。 -
yaml
: 由gopkg.in/yaml.v2
包使用,詳見yaml.Marshal()
。 -
gorm
: 由gorm.io/gorm
包使用,示例可以在 GORM 的文檔中找到。
當然這裏列的就是最常用的幾個庫他們提供給我們使用的結構體標籤,歡迎大夥踊躍留言,補充一些自己平時用過的庫提供給開發者使用的結構體標籤。
總結
這篇文章算是我們上一篇講 Go 反射的一個實踐方向的延伸介紹,如果你也想在自己的包裏提供一些結構體標籤鍵,讓自己的包更易用些,除了看咱們這篇文章外,還可以去看看上面咱們介紹的幾個類庫,看它們的源碼裏是怎麼應用的,現學現用!
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://mp.weixin.qq.com/s/1AVL5k9D55fWIkWm4UPR0Q