web-dev-qa-db-fra.com

Quelles sont les utilisations des étiquettes dans Go?

Dans le Go Language Specification , il est mentionné un bref aperçu des balises:

Une déclaration de champ peut être suivie d'une balise chaîne facultative, qui devient un attribut pour tous les champs de la déclaration de champ correspondante. Les balises sont rendues visibles via une interface de réflexion mais sont par ailleurs ignorées.

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

Ceci est une très courte explication, OMI, et je me demandais si quelqu'un pourrait me donner à quoi serviraient ces balises?

348
liamzebedee

Une balise pour un champ vous permet d’attacher des méta-informations au champ qui peuvent être acquises par réflexion. Il est généralement utilisé pour fournir des informations de transformation sur la manière dont un champ struct est codé ou décodé dans un autre format (ou stocké/extrait d'une base de données), mais vous pouvez l'utiliser pour stocker les méta-informations souhaitées, destinées à un autre forfait ou pour votre propre usage.

Comme indiqué dans la documentation de reflect.StructTag , par convention, la valeur d'une chaîne de balises est une liste de paires _key:"value"_ séparées par un espace, par exemple:

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

key désigne généralement le package auquel le _"value"_ suivant est destiné, par exemple, les clés json sont traitées/utilisées par le package encoding/json .

Si plusieurs informations doivent être transmises dans _"value"_, elles sont généralement spécifiées en les séparant par une virgule (_','_), par exemple.

_Name string `json:"name,omitempty" xml:"name"`
_

Habituellement, une valeur de tiret (_'-'_) pour _"value"_ signifie d'exclure le champ du processus (par exemple, dans le cas de json, cela signifie de ne pas marshaller ou dégrouper ce champ).

Exemple d'accès à vos balises personnalisées à l'aide de la réflexion

Nous pouvons utiliser la réflexion ( reflect package) pour accéder aux valeurs de balise des champs struct. Fondamentalement, nous devons acquérir le Type de notre structure, puis nous pouvons interroger des champs, par exemple. avec Type.Field(i int) ou Type.FieldByName(name string). Ces méthodes retournent une valeur de StructField qui décrit/représente un champ struct; et _StructField.Tag_ est une valeur de type StructTag qui décrit/représente une valeur de balise.

Auparavant, nous parlions de "convention" . Cette convention signifie que si vous la suivez, vous pouvez utiliser la méthode StructTag.Get(key string) qui analyse la valeur d'une balise et vous renvoie le _"value"_ du key que vous spécifiez. La convention est implémentée/intégrée à cette méthode Get(). Si vous ne suivez pas la convention, Get() ne pourra pas analyser _key:"value"_ paires et trouver ce que vous recherchez. Ce n'est pas non plus un problème, mais vous devez ensuite implémenter votre propre logique d'analyse.

Il y a aussi StructTag.Lookup() (a été ajouté dans Go 1.7) qui est "comme Get() mais distingue la balise ne contenant pas la clé donnée de la balise associant une chaîne vide à la clé donnée ".

Voyons un exemple simple:

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

Sortie (essayez-le sur le 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 avait une présentation sur les balises struct appelée:

Les nombreuses facettes des balises Struct (diapositive) (et a video )

Voici une liste des clés de balises couramment utilisées:

546
icza

Voici un exemple très simple d'utilisation de balises avec le package encoding/json pour contrôler la façon dont les champs sont interprétés lors du codage et du décodage:

Essayez en direct: 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"}
_

Le package json peut examiner les balises du champ et savoir comment mapper le champ struct json <=>, ainsi que des options supplémentaires telles que le fait qu'il doit ou non ignorer les champs vides lors de la sérialisation en json.

Fondamentalement, tout package peut utiliser la réflexion sur les champs pour examiner les valeurs de balises et agir sur ces valeurs. Il y a un peu plus d'informations à leur sujet dans le paquet reflect
http://golang.org/pkg/reflect/#StructTag :

Par convention, les chaînes de balises sont une concaténation de paires clé: "valeur", éventuellement séparées par des espaces. Chaque clé est une chaîne non vide composée de caractères de contrôle autres que des espaces (U + 0020 ''), guillemet (U + 0022 '"') et deux points (U + 003A ':'). Chaque valeur est citée. using U + 0022 '"' caractères et la syntaxe littérale de chaîne Go.

150
jdi