[每日一譯] Tags in Golang
原文地址: Tags in Golang
我們宣告golang struct時可以在struct欄位後面新增一些字串來豐富這個欄位, 這些字串稱為tag
. Tags可以被當前package或者包外使用. 讓我們首先看看struct是如何宣告的, 然後深入研究下tag本身, 最後用幾個例子結束
Struct type
struct是一系列的欄位的組合. 每一個欄位都由一個optional
名字和required
type組成
package main
import "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
}
T1 被稱為embedded field, 因為它只有型別沒有名字 (hhf: 關於這個可以看我另外一篇文章: golang物件導向分析)
欄位可以用一個型別宣告多個識別符號, 就像T2的f3, f4一樣
golang語言宣告每個欄位宣告後面跟著分號, 但是我們在寫的時候可以忽略分號(hhf:https://golang.org/doc/effective_go.html#semicolons, if the newline comes after a token that could end a statement, insert a semicolon). 當多個欄位放在同一行時, 需要用分號分割
package main
import "fmt"
type T struct {
f1 int64; f2 float64
}
func main() {
t := T{1, 2}
fmt.Println(t.f1, t.f2) // 1 2
}
Tag
Struct的欄位宣告時, 如果後面跟著可選的tag
時, 那麼tag
將相對應的那些欄位的屬性
type T struct {
f1 string "f one" // 解釋字串
f2 string
f3 string `f three` // 原始字串
f4, f5 int64 `f four and five`
}
tag可以使用原始字串或者解釋字串, 下面描述的傳統格式的例子需要使用原始字串. 原始字串和解釋字串 在spec有詳細解釋
struct欄位同一個type宣告多個識別符號(例如上面的f4,f5), 那麼tag對f4,f5都是有效的
Reflection
Tags是可以通過reflect
被訪問到的.
package main
import (
"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) // ""
}
Conventional format
reflect: support for struct tag use by multiple packages的介紹中允許為每個package設定元資訊. 這提供了簡單的名稱空間. Tags被格式化成key-value鍵值對, Key可以是像json
包這樣的名字. 鍵值對可以被空格分開(這是可選的)--key1:"value1" key2:"value2" key3:"value3"
. 如果使用傳統格式,那麼我們可以使用兩種struct tag方法--Get or Lookup
, 他們返回key對應的value
Lookup
返回兩個值: 與key關聯的值, bool值(是否找到那個key)
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
的簡單封裝, 但是他捨棄掉了bool值
func (tag StructTag) Get(key string) string {
v, _ := tag.Lookup(key)
return v
}
當Tag不是傳統格式時, 那麼
Lookup
和Get
不會返回指定的值 (hhf:這句話是什麼意思呢 Return value of Get or Lookup is unspecified if tag doesn’t have conventional format.)
不管tag是任何字串(原始字串或者解釋字串), 只有value包含在雙引號之間, 那麼LookUp
和Get
會返回key對應的值.
type T struct {
f string "one:`1`"
}
func main() {
t := reflect.TypeOf(T{})
f, _ := t.FieldByName("f")
fmt.Println(f.Tag) // one:`1`
v, ok := f.Tag.Lookup("one")
fmt.Printf("%s, %t\n", v, ok) // , false
}
hhf: 從這裡可以看出來, 只有value包含在""之中才能得到相關的值
可以在雙引號中使用轉義的解釋字串, 但是這個可讀性要差很多
type T struct {
f string "one:\"1\""
}
func main() {
t := reflect.TypeOf(T{})
f, _ := t.FieldByName("f")
fmt.Println(f.Tag) // one:"1"
v, ok := f.Tag.Lookup("one")
fmt.Printf("%s, %t\n", v, ok) // 1, true
}
Conversion
當一個struct向另外一個轉換時, 需要對應欄位的型別是相同的. 但是這些欄位的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}
使用struct tag的例子
(Un)marshaling
golang使用tag最多的地方可能就是marshalling 讓我們看一下來自json包的函式Marshal是如何使用的
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}
}
xml
包也使用了tag的特性—https://golang.org/pkg/encoding/xml/#MarshalIndent.
ORM
像gorm廣泛使用tag example
*hhf: 我覺得最好用的orm
Other
其他潛在的用法可能就是配置管理
, struct預設值
, validation
, 命令列引數
, 例如https://github.com/golang/go/w ... -tags
go vet
Go編譯器不強制執行結構標記的傳統格式,但是去看看它是否值得使用它,例如作為CI管道的一部分
package main
type T struct {
f string "one two three"
}
func main() {}
> go vet tags.go
tags.go:4: struct field tag `one two three` not compatible with reflect.StructTag.Get: bad syntax for struct tag pair
hhf: 一般IDE都直接提示: bad syntax for struct tag
歡迎檢視我的部落格原文地址
相關文章
- Tags in GolangGolang
- 每日一個 Golang PackagesGolangPackage
- Golang每日一庫之regexGolang
- Golang每日一庫之bcryptGolang
- 每日一個 Golang Packages 06/11 syncGolangPackage
- 每日一個 Golang Packages 06/10 runtimeGolangPackage
- 每日一個 Golang Packages 06/04 timeGolangPackage
- 每日一個 Golang Packages 06/05 stringsGolangPackage
- 每日一個 Golang Packages 06/09 mathGolangPackage
- 每日一個 Golang Packages 06/12 fmtGolangPackage
- 每日一個 Golang Packages 06/08 os ioutilGolangPackage
- 每日一個 Golang Packages 06/06 os FileGolangPackage
- 每日一個 Golang Packages 06/13 math.randGolangPackage
- 每日一個 Golang Packages 06/07 os File Read wtiteGolangPackage
- golang交叉編譯Golang編譯
- [譯] part 7: golang 包Golang
- [譯] part 21: golang GoroutinesGolang
- [譯] part 29: golang deferGolang
- [譯]用Golang編寫一個簡易聊天室Golang
- golang初學:交叉編譯Golang編譯
- [譯] part 14: golang 函式Golang函式
- [譯] part22: golang channelsGolang
- [譯] part 17: golang 方法methodsGolang
- [譯] part24: golang selectGolang
- [譯] part 19: golang 介面 2Golang
- [譯] part 34: golang 反射 reflectionGolang反射
- Golang 編譯windows應用程式Golang編譯Windows
- [譯] part 15: golang 指標pointersGolang指標
- [譯] part18: golang 介面 1Golang
- [譯] Golang 資料結構:樹Golang資料結構
- [譯] part 13: golang 對映 mapGolang
- TailWind文件翻譯說明以及每日翻譯進度AI
- 用 golang 寫一個語言(編譯器,虛擬機器)Golang編譯虛擬機
- 每日一練(一)
- 走進Golang之編譯器原理Golang編譯
- [譯] part 9: golang 迴圈語句Golang
- [譯] part 20: golang 併發介紹Golang
- [譯] part 16: golang 結構體structuresGolang結構體Struct