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


392

ゴー言語仕様、それはタグの簡単な概要を述べて:

フィールド宣言の後には、オプションの文字列リテラルタグが続く場合があります。これは、対応するフィールド宣言のすべてのフィールドの属性になります。タグは反射インターフェースを介して表示されますが、それ以外の場合は無視されます。

// 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です。これらのタグの用途を誰かに教えてもらえないかと思いました。


:私は「セマンティックコメントの用途に関連する質問持つstackoverflow.com/questions/53101458/...
ブルース・アダムス

回答:


641

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

のドキュメントで説明されているように、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パッケージ)を使用して、構造体フィールドのタグ値にアクセスできます。基本的にTypeは、構造体のを取得する必要があります。それから、たとえばType.Field(i int)またはでフィールドを照会できますType.FieldByName(name string)。これらのメソッドStructFieldは、構造体フィールドを説明/表す値を返します。そしてStructField.Tagタイプの値であり、StructTag/は、タグ値を示し説明しています。

以前は「慣習」について話しました。この規則は、それに従う場合、StructTag.Get(key string)タグの値を解析して指定したの"value"を返すメソッドを使用できることを意味しkeyます。大会は、このに組み込ま/実装されているGet()方法。規則に従わない場合、ペアGet()を解析key:"value"して探しているものを見つけることができなくなります。これも問題ではありませんが、独自の解析ロジックを実装する必要があります。

また、StructTag.Lookup() Go 1.7で追加された」「Get()指定されたキーを含まないタグと空の文字列を指定されたキーに関連付けるタグを区別する」もあります。

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

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

u := User{"Bob", "bob@mycompany.com"}
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"))
}

出力(上でそれを試してみてくださいゴー遊び場):

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には、次の名前の構造体タグに関するプレゼンテーションがありました。

構造タグの多くの面(スライド)(およびビデオ

一般的に使用されるタグキーのリストを次に示します。


28
すばらしい答えです。このカルマの10倍のカルマよりも、はるかに役立つ情報がここにあります。
Darth Egregious

2
とてもいいまとめ!
stevenferrer 2017年

2
なんて素晴らしい答えでしょう
AlbertoMegía17年

1
正解です。ありがとうございました!
JumpAlways 2017年

1
すばらしい答えです。この情報をすべてありがとうございます。
サムホームズ

157

以下は、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 <=>構造体フィールドをマップする方法や、jsonにシリアル化するときに空のフィールドを無視するかどうかなどの追加オプションを通知できます。

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

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


6
Javaアノテーションのようなものですか?
Ismail Badawi

7
@isbadawi:私はJavaの人ではありませんが、Javaアノテーションの定義を一目見ただけで、同じ目標を達成しているようです。実行時に検査できる要素にメタデータを添付する。
jdi

15
実際にはJavaアノテーションではありません。Javaアノテーションはタイプセーフで、コンパイル時にチェックされます。goのような文字列リテラルではありません。Javaアノテーションは、golangの基本的なメタデータ規定よりもはるかに強力で堅牢です。
座っ

2
GoのMongoDBドライバーの一部として、mgoは、bsonパッケージ(単独で使用することもできます)のタグも使用します。生成されるBSONを正確に制御できます。godoc.org/labix.org/v2/mgo/bson#pkg-filesを
Eno

1
JSONとBSON以外の例はありますか?
Max Heiber

1

タグ付けされたフィールドをパッケージがどのように処理するかを指定するのは、ある種の仕様です。

例えば:

type User struct {
    FirstName string `json:"first_name"`
    LastName string `json:"last_name"`
}

jsonタグはjson、次のユーザーの出力をマーシャリングしたことをパッケージに通知します

u := User{
        FirstName: "some first name",
        LastName:  "some last name",
    }

次のようになります:

{"first_name":"some first name","last_name":"some last name"}

他の例は、gormパッケージタグがデータベースの移行を行う方法を宣言するものです。

type User struct {
  gorm.Model
  Name         string
  Age          sql.NullInt64
  Birthday     *time.Time
  Email        string  `gorm:"type:varchar(100);unique_index"`
  Role         string  `gorm:"size:255"` // set field size to 255
  MemberNumber *string `gorm:"unique;not null"` // set member number to unique and not null
  Num          int     `gorm:"AUTO_INCREMENT"` // set num to auto incrementable
  Address      string  `gorm:"index:addr"` // create index with name `addr` for address
  IgnoreMe     int     `gorm:"-"` // ignore this field
}

この例ではEmail、gormタグが付いたフィールドで、フィールドemailのデータベース内の対応する列のタイプがvarcharで最大長が100であり、一意のインデックスも必要であることを宣言しています。

他の例はbindingginパッケージで非常に多く使用されるタグです。

type Login struct {
    User     string `form:"user" json:"user" xml:"user"  binding:"required"`
    Password string `form:"password" json:"password" xml:"password" binding:"required"`
}


var json Login
if err := c.ShouldBindJSON(&json); err != nil {
     c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
     return
}

この例のバインディングタグは、APIに送信されるデータにユーザーフィールドとパスワードフィールドが必要であり、これらのフィールドが必要に応じてタグ付けされていることをginパッケージに示唆しています。

したがって、一般的なタグは、パッケージがタイプの異なる構造体のデータをどのように扱うかを知るためにパッケージが必要とするデータであり、パッケージが必要とするタグに精通する最良の方法は、パッケージドキュメントの完全な読み取りです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.