516

Go言語仕様では、タグの簡単な概要について説明しています。

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

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

4

3 に答える 3

865

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

のドキュメントで説明されているように、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] 6の値であり、タグ値を記述/表します。

以前、 「コンベンション」について話しました。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"))
}

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

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

一般的に使用されるタグキーのリストは次のとおりです。

于 2015-06-17T10:49:23.137 に答える
163

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文字列リテラル構文を使用します。

于 2012-06-02T00:24:34.900 に答える
2

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

例えば:

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
}

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

他の例は、パッケージbindingで非常に主に使用されるタグです。gin

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パッケージに提供します。これにより、これらのフィールドは必要に応じてタグ付けされます。

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

于 2020-06-03T18:45:46.257 に答える