web-dev-qa-db-de.com

Wozu dienen Tags in Go?

In der Go Language Specification wird eine kurze Übersicht der Tags erwähnt:

Auf eine Felddeklaration kann ein optionales String-Literal-Tag folgen, das zu einem Attribut für alle Felder in der entsprechenden Felddeklaration wird. Die Tags werden über eine Reflektionsschnittstelle sichtbar gemacht, ansonsten jedoch ignoriert.

// 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"
}

Dies ist eine sehr kurze Erklärung, IMO, und ich habe mich gefragt, ob mir jemand mitteilen könnte, wozu diese Tags dienen würden.

348
liamzebedee

Mit einem Tag für ein Feld können Sie Metainformationen an das Feld anhängen, die durch Reflektion erfasst werden können. Normalerweise wird es verwendet, um Transformationsinformationen darüber bereitzustellen, wie ein Strukturfeld in ein anderes Format codiert oder von einem anderen Format decodiert (oder aus einer Datenbank gespeichert/abgerufen) wird. Sie können es jedoch verwenden, um beliebige Metainformationen zu speichern, die entweder für ein anderes bestimmt sind Paket oder für Ihren eigenen Gebrauch.

Wie in der Dokumentation von reflect.StructTag erwähnt, ist der Wert einer Tag-Zeichenfolge standardmäßig eine durch Leerzeichen getrennte Liste von key:"value" - Paaren. Beispiel:

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

Das key bezeichnet normalerweise das Paket, für das das nachfolgende "value" Bestimmt ist, zum Beispiel json Schlüssel werden von encoding/json Verarbeitet/verwendet = Paket.

Wenn im "value" Mehrere Informationen übergeben werden sollen, werden diese normalerweise durch Trennen mit einem Komma (',') Angegeben, z.

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

Normalerweise bedeutet ein Strichwert ('-') Für "value", Dass das Feld vom Prozess ausgeschlossen wird (z. B. im Fall von json, dass dieses Feld nicht gemarshallt oder gemarshallt wird).

Beispiel für den Zugriff auf Ihre benutzerdefinierten Tags mithilfe von Reflection

Wir können Reflection ( reflect package) verwenden, um auf die Tag-Werte von Strukturfeldern zuzugreifen. Grundsätzlich müssen wir das Type unserer Struktur erhalten, und dann können wir Felder abfragen, z. mit Type.Field(i int) oder Type.FieldByName(name string). Diese Methoden geben den Wert StructField zurück, der ein Strukturfeld beschreibt/darstellt. und StructField.Tag ist ein Wert vom Typ StructTag , der einen Tag-Wert beschreibt/darstellt.

Zuvor sprachen wir über "Konvention". Diese Konvention besagt, dass Sie die Methode StructTag.Get(key string) verwenden können, die den Wert eines Tags analysiert und Ihnen den "value" Des key Sie geben an. Die Konvention ist in diese Get() Methode implementiert/eingebaut. Wenn Sie sich nicht an die Konvention halten, kann Get() keine key:"value" - Paare analysieren und finden, wonach Sie suchen. Das ist auch kein Problem, aber dann müssen Sie Ihre eigene Parsing-Logik implementieren.

Es gibt auch StructTag.Lookup() (wurde in Go 1.7 hinzugefügt), das "wie Get() ist, aber das Tag, das den angegebenen Schlüssel nicht enthält, von dem unterscheidet Tag, das eine leere Zeichenfolge mit dem angegebenen Schlüssel verknüpft ".

Schauen wir uns also ein einfaches Beispiel an:

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"))
}

Ausgabe (versuchen Sie es auf dem 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"

Auf der GopherCon 2015 gab es eine Präsentation zu Struktur-Tags mit dem Namen:

Die vielen Gesichter von Struct-Tags (Folie) (und ein Video )

Hier ist eine Liste häufig verwendeter Tag-Schlüssel:

546
icza

Hier ist ein wirklich einfaches Beispiel für Tags, die mit dem Paket encoding/json verwendet werden, um zu steuern, wie Felder während der Codierung und Decodierung interpretiert werden:

Versuchen Sie es live: 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"}

Das json-Paket kann die Tags für das Feld anzeigen und erfahren, wie das json <=> struct-Feld zugeordnet wird, sowie zusätzliche Optionen, z. B., ob leere Felder bei der Serialisierung zurück zu json ignoriert werden sollen.

Grundsätzlich kann jedes Paket die Felder reflektieren, um Tag-Werte zu betrachten und auf diese Werte zu reagieren. Es gibt ein wenig mehr Informationen darüber im Reflect-Paket
http://golang.org/pkg/reflect/#StructTag :

Gemäß der Konvention sind Tag-Zeichenfolgen eine Verkettung von optional durch Leerzeichen getrennten Schlüssel: "Wert" -Paaren. Jeder Schlüssel ist eine nicht leere Zeichenfolge, die aus anderen Nicht-Steuerzeichen als Leerzeichen (U + 0020 ''), Anführungszeichen (U + 0022 '"') und Doppelpunkt (U + 003A ':') besteht. Jeder Wert wird in Anführungszeichen gesetzt Verwenden von U + 0022-Zeichen und Go-String-Literal-Syntax.

150
jdi