盤點一下結構體標籤在Go中的應用
掌握了Go
語言的朋友們應該都知道,在Go
的結構體型別聲明裡面,欄位聲明後可以跟一個可選的字串標籤。
type User struct {
Name string `json:"name"`
}
上面是一個標準的例子,Name
欄位宣告中指定了標籤json:"name" xml:"name"
,這個標籤值看著有點類似Java
程式裡給類屬性加的註解。
那麼這些結構體標籤有什麼用途呢,我們隨便寫管用嗎?我們平時工作中常用的結構體標籤有哪些呢?我們能不能自己定義結構體標籤?今天就帶大家掰扯清楚這些問題!
結構體標籤
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", "[email protected]"}
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反射的一個實踐方向的延伸介紹,如果你也想在自己的包裡提供一些結構體標籤鍵,讓自己的包更易用些,除了看咱們這篇文章外,還可以去看看上面咱們介紹的幾個類庫,看它們的原始碼裡是怎麼應用的,現學現用!
json中omitempty欄位的使用 原創
2021-09-10 15:04:31 關注總結
1. omitempty是省略的意思
2. json中欄位若有omitempty標記,則這個欄位為空時,json序列化為string時不會包含該欄位
3. json中欄位若沒有omitempty標記,則這個欄位為空時,json序列化為string時會包含該欄位
看程式碼
- package main
- import (
- "encoding/json"
- "fmt"
- )
- // 學生資訊
- type Student struct {
- Id int `json:"id"`
- Name string `json:"name,omitempty"`
- }
- func main() {
- // 測試第一個(Name欄位賦值,序列化後這個欄位是存在的)
- s1 := Student{
- Id:1,
- Name:"張三",
- }
- data1, _ := json.Marshal(s1)
- fmt.Printf("%s\n", data1)
- // 測試第二個(Id欄位不賦值,序列化後Id欄位仍然存在;Name欄位不賦值,序列化後這個欄位就不存在了)
- s2 := Student{
- //Id:2,
- //Name:"李四",
- }
- data2, _ := json.Marshal(s2)
- fmt.Printf("%s\n", data2)
- }
實驗如下