我們可以通過Tag
來增強結構體的定義,Tag會帶上一些meta資訊,在本文中我們將通過幾個例子來深入瞭解Tag
的用法。
結構
Struct
是由一組field
組成,每個field
包括了名字(可選)和欄位類型
package mainimport "fmt"type T1 struct { f1 string}type T2 struct { T1 f2 int64 f3, f4 float64}func main() { t := T2{T1{"foo"}, 1, 2, 3} fmt.Println(t.f1) // foo fmt.Println(t.T1.f1) // foo fmt.Println(t.f2) // 1}
field
T1是一個嵌入型field
, 它並沒有欄位名 。Field
定義可以像f3
和f4
一樣公用一個欄位類型。
標籤
有時候會在欄位定義後面帶上一個字串(tag
)。類似於如下
type T struct { f1 string "f one" f2 string f3 string `f three` f4, f5 int64 `f four and five`}
不管是raw string
還是interpreted string
都可以用來當tag
。 如果field
定義時候兩個名字公用一個屬性,那麼這個tag
會被附在兩個名字上,像f4,f5一樣。
反射
Tag
在運行時可以通過reflection
包來讀取
package mainimport ( "fmt" "reflect")type T struct { f1 string "f one" f2 string f3 string `f three` f4, f5 int64 `f four and five`}func main() { t := reflect.TypeOf(T{}) f1, _ := t.FieldByName("f1") fmt.Println(f1.Tag) // f one f4, _ := t.FieldByName("f4") fmt.Println(f4.Tag) // f four and five f5, _ := t.FieldByName("f5") fmt.Println(f5.Tag) // f four and five}
設定一個空tag
和不設定tag
的效果一致
type T struct { f1 string `` f2 string}func main() { t := reflect.TypeOf(T{}) f1, _ := t.FieldByName("f1") fmt.Printf("%q\n", f1.Tag) // "" f2, _ := t.FieldByName("f2") fmt.Printf("%q\n", f2.Tag) // ""}
格式
Tags
可以由索引值對來組成,通過空格符來分割索引值 —key1:"value1" key2:"value2" key3:"value3"
。如果Tags格式沒問題的話,我們可以通過Lookup
或者Get
來擷取索引值對的值。
Lookup
回傳兩個值 —對應的值和是否找到
type T struct { f string `one:"1" two:"2"blank:""`}func main() { t := reflect.TypeOf(T{}) f, _ := t.FieldByName("f") fmt.Println(f.Tag) // one:"1" two:"2"blank:"" v, ok := f.Tag.Lookup("one") fmt.Printf("%s, %t\n", v, ok) // 1, true v, ok = f.Tag.Lookup("blank") fmt.Printf("%s, %t\n", v, ok) // , true v, ok = f.Tag.Lookup("five") fmt.Printf("%s, %t\n", v, ok) // , false}
Get
方法只是簡單的封裝了以下Lookup
。但是丟棄了是否成功結果
func (tag StructTag) Get(key string) string { v, _ := tag.Lookup(key) return v}
轉化
將結構體的值轉化成其他的類型可通過Tag來定義
type T1 struct { f int `json:"foo"` } type T2 struct { f int `json:"bar"` } t1 := T1{10} var t2 T2 t2 = T2(t1) fmt.Println(t2) // {10}
什麼時候用到了Tag
(Un)marshaling
Tag最常用的大概就是在marshaling
。
import ( "encoding/json" "fmt")func main() { type T struct { F1 int `json:"f_1"` F2 int `json:"f_2,omitempty"` F3 int `json:"f_3,omitempty"` F4 int `json:"-"` } t := T{1, 0, 2, 3} b, err := json.Marshal(t) if err != nil { panic(err) } fmt.Printf("%s\n", b) // {"f_1":1,"f_3":2}}
ORM
比如說GORM— example.
go vet
go的編譯器不會強行要求你使用合理的tags
。但是 go vet可以檢查出你的tag
是否合理。
package maintype T struct { f string "one two three"}func main() {}> go vet tags.gotags.go:4: struct field tag `one two three` not compatible with reflect.StructTag.Get: bad syntax for struct tag pair