web-dev-qa-db-ja.com

Goのタグの用途は何ですか?

Go Language Specification では、タグの概要を説明しています。

フィールド宣言の後には、オプションの文字列リテラルタグを続けることができます。これは、対応するフィールド宣言内のすべてのフィールドの属性になります。タグはリフレクションインタフェースを通して表示されますが、それ以外は無視されます。

// A struct corresponding to the TimeStamp protocol buffer.
// The tag strings define the protocol buffer field numbers.
struct {
  microsec  uint64 "field 1"
  serverIP6 uint64 "field 2"
  process   string "field 3"
}

これはIMOの非常に短い説明です、そして、誰かがこれらのタグの用途が何かを私に提供することができるかどうか疑問に思いましたか?

336
liamzebedee

フィールドのタグを使用すると、リフレクションを使用して取得できるフィールドにメタ情報を添付することができます。通常、structフィールドが他のフォーマットにエンコードされたりデコードされたりする方法(またはデータベースに格納/取得される方法)に関する変換情報を提供するために使用されますが、目的のメタ情報を格納するために使用できます。パッケージまたはあなた自身の使用のために。

reflect.StructTag のドキュメントで述べたように、慣例により、タグ文字列の値はkey:"value"ペアのスペース区切りのリストです。次に例を示します。

type User struct {
    Name string `json:"name" xml:"name"`
}

keyは通常、後続の"value"が対象とするパッケージを表します。たとえば、jsonキーは encoding/json パッケージによって処理または使用されます。

複数の情報を"value"に渡す場合は、通常、カンマ(',')で区切って指定します。

Name string `json:"name,omitempty" xml:"name"`

通常、'-'のダッシュ値("value")はプロセスからフィールドを除外することを意味します(例えばjsonの場合、それはそのフィールドを整列化または非整列化しないことを意味します)。

リフレクションを使用してカスタムタグにアクセスする例

リフレクション( reflect package)を使って構造体フィールドのタグ値にアクセスすることができます。基本的には、構造体の Type を取得する必要があります。それからフィールドを問い合わせることができますType.Field(i int)またはType.FieldByName(name string)と一緒に。これらのメソッドは StructField という値を返します。 StructField.TagStructTag というタイプの値で、タグ値を記述したり表します。

以前は「規約」について説明しました。この規則は、それに従えば、タグの値を解析して指定したkey"value"を返す StructTag.Get(key string) メソッドを使用できることを意味します。 の規則はこのGet()メソッドに実装されています。規約に従わないと、Get()key:"value"ペアを解析して探しているものを見つけることができなくなります。これも問題ではありませんが、それからあなたはあなた自身の解析ロジックを実装する必要があります。

StructTag.Lookup()のように "である Get() (Go 1.7で追加された)もありますが、与えられたキーを含まないタグと空の文字列を関連付けるタグを区別します"

簡単な例を見てみましょう。

type User struct {
    Name  string `mytag:"MyName"`
    Email string `mytag:"MyEmail"`
}

u := User{"Bob", "[email protected]"}
t := reflect.TypeOf(u)

for _, fieldName := range []string{"Name", "Email"} {
    field, found := t.FieldByName(fieldName)
    if !found {
        continue
    }
    fmt.Printf("\nField: User.%s\n", fieldName)
    fmt.Printf("\tWhole tag value : %q\n", field.Tag)
    fmt.Printf("\tValue of 'mytag': %q\n", field.Tag.Get("mytag"))
}

出力( Go Playground )で試してください。

Field: User.Name
    Whole tag value : "mytag:\"MyName\""
    Value of 'mytag': "MyName"

Field: User.Email
    Whole tag value : "mytag:\"MyEmail\""
    Value of 'mytag': "MyEmail"

GopherCon 2015では、次の構造体タグについての発表がありました。

構造タグのさまざまな面(スライド)(および ビデオ

これはよく使われるタグキーのリストです:

523
icza

これは encoding/json パッケージで使われているタグの本当に簡単な例です。

ライブで試す: http://play.golang.org/p/BMeR8p1cKf

package main

import (
    "fmt"
    "encoding/json"
)

type Person struct {
    FirstName  string `json:"first_name"`
    LastName   string `json:"last_name"`
    MiddleName string `json:"middle_name,omitempty"`
}

func main() {
    json_string := `
    {
        "first_name": "John",
        "last_name": "Smith"
    }`

    person := new(Person)
    json.Unmarshal([]byte(json_string), person)
    fmt.Println(person)

    new_json, _ := json.Marshal(person)
    fmt.Printf("%s\n", new_json)
}

// *Output*
// &{John Smith }
// {"first_name":"John","last_name":"Smith"}

Jsonパッケージはフィールドのタグを調べて、json <=> structフィールドをマップする方法、およびjsonにシリアライズするときに空のフィールドを無視するかどうかなどの追加オプションを指示することができます。

基本的に、どのパッケージでもフィールドのリフレクションを使用してタグ値を調べ、それらの値に基づいて動作できます。リフレクトパッケージにはもう少し詳しい情報があります
http://golang.org/pkg/reflect/#StructTag

慣例により、タグ文字列は、オプションでスペースで区切られたkey: "value"のペアの連結です。各キーは、スペース(U + 0020 '')、引用符(U + 0022 '"')、およびコロン(U + 003A ':')以外の非制御文字で構成される、空ではない文字列です。 U + 0022 '"'文字とGo文字列リテラル構文を使用する。

148
jdi