Quali sono gli usi dei tag in Go?


392

In Go Language Specification , menziona una breve panoramica dei tag:

Una dichiarazione di campo può essere seguita da un tag letterale stringa facoltativo, che diventa un attributo per tutti i campi nella dichiarazione di campo corrispondente. I tag sono resi visibili attraverso un'interfaccia di riflessione ma vengono altrimenti ignorati.

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

Questa è una spiegazione molto breve dell'IMO, e mi chiedevo se qualcuno potesse fornirmi quale uso sarebbero questi tag?


Ho una domanda correlata per l'uso dei commenti "semantici": stackoverflow.com/questions/53101458/…
Bruce Adams,

La correzione del collegamento dovrebbe essere stackoverflow.com/q/53487371/1569204
Bruce Adams,

Risposte:


641

Un tag per un campo consente di allegare meta-informazioni al campo che possono essere acquisite mediante la riflessione. Di solito viene utilizzato per fornire informazioni di trasformazione su come un campo struct viene codificato o decodificato da un altro formato (o archiviato / recuperato da un database), ma è possibile utilizzarlo per archiviare qualsiasi meta-informazione desiderata, sia destinata a un altro pacchetto o per uso personale.

Come menzionato nella documentazione di reflect.StructTag, per convenzione, il valore di una stringa tag è un elenco di key:"value"coppie separate da spazi , ad esempio:

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

Di keysolito indica il pacchetto a cui fa riferimento il successivo "value", ad esempio le jsonchiavi vengono elaborate / utilizzate dal encoding/jsonpacchetto.

Se devono essere passate più informazioni in "value", di solito vengono specificate separandole con una virgola ( ','), ad es

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

Di solito un valore di trattino ( '-') per i "value"mezzi per escludere il campo dal processo (ad esempio, nel caso in cui jsonsignifichi non eseguire il marshalling o annullare il ripristino di quel campo).

Esempio di accesso ai tag personalizzati tramite reflection

Possiamo usare reflection ( reflectpacchetto) per accedere ai valori dei tag dei campi struct. Fondamentalmente dobbiamo acquisire la Typenostra struttura e quindi possiamo interrogare i campi, ad esempio con Type.Field(i int)o Type.FieldByName(name string). Questi metodi restituiscono un valore di StructFieldcui descrive / rappresenta un campo struct; ed StructField.Tagè un valore di tipo StructTagche descrive / rappresenta un valore di tag.

In precedenza abbiamo parlato di "convenzione" . Questo significa convenzione che se si seguono, si può usare il StructTag.Get(key string)metodo che analizza il valore di una variabile e restituisce la "value"del keyspecificato. La convenzione è implementata / integrata in questo Get()metodo. Se non segui la convenzione, Get()non sarai in grado di analizzare le key:"value"coppie e trovare quello che stai cercando. Anche questo non è un problema, ma è necessario implementare la propria logica di analisi.

Inoltre c'è StructTag.Lookup()( è stato aggiunto in Go 1.7) che è "simile Get()ma distingue il tag che non contiene la chiave data dal tag che associa una stringa vuota alla chiave data" .

Quindi vediamo un semplice esempio:

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

Output (provalo sul 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 ha presentato una presentazione sui tag struct chiamata:

I molti volti dei tag Struct (slide) (e un video )

Ecco un elenco di chiavi tag comunemente usate:


28
Risposta eccellente. Informazioni molto più utili qui che in quella con dieci volte questo karma.
Darth Egregious,

2
riassunto molto bello!
Stevferfer

2
Che risposta fantastica
Alberto Megía,

1
Bella risposta! Grazie!
JumpAlways

1
Risposta incredibile, grazie per tutte queste informazioni!
Sam Holmes,

157

Ecco un esempio molto semplice di tag utilizzati con il encoding/jsonpacchetto per controllare il modo in cui i campi vengono interpretati durante la codifica e la decodifica:

Prova dal vivo: 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"}

Il pacchetto json può esaminare i tag per il campo e ricevere istruzioni su come mappare il campo struct <=> json e anche opzioni extra come se debba ignorare i campi vuoti durante la serializzazione su json.

Fondamentalmente, qualsiasi pacchetto può usare la riflessione sui campi per guardare i valori dei tag e agire su questi valori. Ci sono alcune informazioni in più su di loro nel pacchetto di riflessione
http://golang.org/pkg/reflect/#StructTag :

Per convenzione, le stringhe di tag sono una concatenazione di chiavi facoltativamente separate dallo spazio: coppie "valore". Ogni chiave è una stringa non vuota composta da caratteri non di controllo diversi dallo spazio (U + 0020 ''), virgoletta (U + 0022 '"') e due punti (U + 003A ':'). Ogni valore viene citato usando i caratteri "+" U + 0022 e la sintassi letterale della stringa Go.


6
Un po 'come le annotazioni Java?
Ismail Badawi,

7
@isbadawi: non sono un tipo java, ma a colpo d'occhio la definizione delle annotazioni java, sì, sembra che stiano raggiungendo lo stesso obiettivo; allegare metadati a elementi che possono essere esaminati in fase di esecuzione.
jdi

15
Non proprio annotazioni Java. Le annotazioni Java sono di tipo sicuro e il tempo di compilazione è controllato, non i letterali di stringa come go. Le annotazioni Java sono molto più potenti e robuste delle disposizioni di base sui metadati.
sabato

2
Come parte del driver MongoDB per Go, mgo utilizza anche tag nel suo pacchetto bson (che può anche essere usato da solo). Ti dà un controllo preciso su ciò che viene generato BSON. Vedi godoc.org/labix.org/v2/mgo/bson#pkg-files
Eno

1
Ci sono altri esempi oltre a JSON e BSON?
Max Heiber,

1

È una sorta di specifica che specifica come i pacchetti trattano con un campo che è taggato.

per esempio:

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

Il tag json informa il jsonpacchetto che ha marshalling l'output del seguente utente

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

sarebbe così:

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

un altro esempio è gormche i tag del pacchetto dichiarano come devono essere eseguite le migrazioni del database:

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
}

In questo esempio per il campo Emailcon tag gorm dichiariamo che la colonna corrispondente nel database per il campo email deve essere di tipo varchar e 100 di lunghezza massima e deve anche avere un indice univoco.

l'altro esempio sono i bindingtag che sono usati principalmente nel ginpacchetto.

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
}

il tag di associazione in questo esempio fornisce un suggerimento al pacchetto gin che i dati inviati all'API devono avere campi utente e password perché questi campi sono taggati come richiesto.

Quindi i tag generraly sono dati che i pacchetti richiedono per sapere come devono essere trattati con dati di tipo diverso e il modo migliore per acquisire familiarità con i tag di cui un pacchetto ha bisogno è LEGGERE COMPLETAMENTE UNA DOCUMENTAZIONE DEI PACCHETTI.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.