Introduzione
Le strutture, o struct, sono utilizzate per raccogliere più informazioni insieme in un’unica unità. Queste raccolte di informazioni sono utilizzate per descrivere concetti di livello superiore, come un Indirizzo
composto da una Strada
, Città
, Stato
e CodicePostale
. Quando si leggono queste informazioni da sistemi come database o API, è possibile utilizzare tag delle strutture per controllare come queste informazioni vengono assegnate ai campi di una struttura. I tag delle strutture sono piccoli pezzi di metadati allegati ai campi di una struttura che forniscono istruzioni ad altro codice Go che lavora con la struttura.
Come Appare un Tag di Struttura?
I tag delle strutture Go sono annotazioni che appaiono dopo il tipo in una dichiarazione di struttura Go. Ogni tag è composto da brevi stringhe associate a un valore corrispondente.
A struct tag looks like this, with the tag offset with backtick `
characters:
Altro codice Go è quindi in grado di esaminare queste strutture ed estrarre i valori assegnati a chiavi specifiche richieste. I tag delle strutture non hanno effetto sul funzionamento del tuo codice senza codice aggiuntivo che li esamina.
Prova questo esempio per vedere come appaiono i tag di struttura e che senza codice da un altro pacchetto, non avranno alcun effetto.
Questo produrrà in output:
OutputHi! My name is Sammy
Questo esempio definisce un tipo User
con un campo Name
. Il campo Name
è stato dotato di un tag di struttura di example:"name"
. Ci riferiremmo a questo specifico tag in conversazione come il “tag di struttura di esempio” perché utilizza la parola “example” come sua chiave. Il tag di struttura example
ha il valore "name"
per il campo Name
. Sul tipo User
, definiamo anche il metodo String()
richiesto dall’interfaccia fmt.Stringer
. Questo verrà chiamato automaticamente quando passiamo il tipo a fmt.Println
e ci dà la possibilità di produrre una versione ben formattata della nostra struttura.
All’interno del corpo di main
, creiamo una nuova istanza del nostro tipo User
e la passiamo a fmt.Println
. Anche se la struttura aveva un tag di struttura presente, vediamo che non ha alcun effetto sul funzionamento di questo codice Go. Si comporterà esattamente allo stesso modo se il tag di struttura non fosse presente.
Per utilizzare i tag di struttura per realizzare qualcosa, deve essere scritto altro codice Go per esaminare le strutture durante l’esecuzione. La libreria standard ha pacchetti che utilizzano i tag di struttura come parte della loro operazione. Il più popolare di questi è il pacchetto encoding/json
.
Codifica JSON
JavaScript Object Notation (JSON) è un formato testuale per codificare raccolte di dati organizzati sotto diversi chiavi di stringa. È comunemente usato per comunicare dati tra diversi programmi poiché il formato è abbastanza semplice da permettere l’esistenza di librerie per decodificarlo in molti linguaggi diversi. Di seguito è riportato un esempio di JSON:
{
"language": "Go",
"mascot": "Gopher"
}
Questo oggetto JSON contiene due chiavi, linguaggio
e mascotte
. Dopo queste chiavi ci sono i valori associati. Qui la chiave linguaggio
ha un valore di Go
e mascotte
è assegnata il valore Gopher
.
L’encoder JSON nella libreria standard fa uso di tag di struttura come annotazioni che indicano all’encoder come si desidera denominare i campi nell’output JSON. Questi meccanismi di codifica e decodifica JSON possono essere trovati nel encoding/json
pacchetto.
Prova questo esempio per vedere come viene codificato JSON senza tag di struttura:
Questo stampa il seguente output:
Output{
"Name": "Sammy the Shark",
"Password": "fisharegreat",
"CreatedAt": "2019-09-23T15:50:01.203059-04:00"
}
Abbiamo definito una struttura che descrive un utente con campi che includono il loro nome, la password e l’ora in cui l’utente è stato creato. All’interno della funzione main
, creiamo un’istanza di questo utente fornendo valori per tutti i campi tranne PreferredFish
(Sammy piace tutti i pesci). Quindi abbiamo passato l’istanza di User
alla funzione json.MarshalIndent
. Questo è usato in modo che possiamo vedere più facilmente l’output JSON senza utilizzare un tool di formattazione esterno. Questa chiamata potrebbe essere sostituita con json.Marshal(u)
per stampare JSON senza alcuno spazio bianco aggiuntivo. Gli altri due argomenti aggiuntivi per json.MarshalIndent
controllano il prefisso dell’output (che abbiamo omesso con la stringa vuota) e i caratteri da utilizzare per l’indentazione, che qui sono due spazi. Eventuali errori prodotti da json.MarshalIndent
vengono registrati e il programma termina utilizzando os.Exit(1)
. Infine, convertiamo il []byte
restituito da json.MarshalIndent
in una stringa
e passiamo la stringa risultante a fmt.Println
per la stampa sul terminale.
I campi della struttura appaiono esattamente come nominati. Questo non è lo stile JSON tipico che ci si potrebbe aspettare, che utilizza la notazione a cammello per i nomi dei campi. Cambieremo i nomi dei campi per seguire lo stile a cammello in questo prossimo esempio. Come vedrai quando eseguirai questo esempio, questo non funzionerà perché i nomi dei campi desiderati entrano in conflitto con le regole di Go sui nomi dei campi esportati.
Questo presenterà l’output seguente:
Output{}
In questa versione, abbiamo modificato i nomi dei campi in camel case. Ora Name
è name
, Password
è password
, e infine CreatedAt
è createdAt
. All’interno del corpo di main
abbiamo cambiato l’istanza della nostra struttura per utilizzare questi nuovi nomi. Passiamo quindi la struttura alla funzione json.MarshalIndent
come prima. L’output, questa volta, è un oggetto JSON vuoto, {}
.
La corretta conversione in camel case dei campi richiede che il primo carattere sia in minuscolo. Anche se JSON non si preoccupa di come si chiamano i campi, Go sì, poiché indica la visibilità del campo al di fuori del pacchetto. Poiché il pacchetto encoding/json
è un pacchetto separato dal pacchetto main
che stiamo usando, dobbiamo mettere in maiuscolo il primo carattere per renderlo visibile a encoding/json
. Sembra che siamo in un vicolo cieco. Abbiamo bisogno di un modo per comunicare all’encoder JSON come vorremmo che questo campo fosse chiamato.
Utilizzo dei tag di struttura per controllare l’encoding
Puoi modificare l’esempio precedente per avere campi esportati che sono correttamente codificati con nomi di campo in stile camel-case annotando ciascun campo con un tag di struttura. Il tag di struttura che riconosce encoding/json
ha una chiave di json
e un valore che controlla l’output. Posizionando la versione in camel-case dei nomi dei campi come valore della chiave json
, l’encoder utilizzerà quel nome invece. Questo esempio corregge i due tentativi precedenti:
Questo produrrà in output:
Output{
"name": "Sammy the Shark",
"password": "fisharegreat",
"preferredFish": null,
"createdAt": "2019-09-23T18:16:17.57739-04:00"
}
Abbiamo cambiato i campi della struttura per renderli visibili ad altri pacchetti capitalizzando le prime lettere dei loro nomi. Tuttavia, questa volta abbiamo aggiunto tag di struttura nella forma di json:"nome"
, dove "nome"
era il nome che volevamo che json.MarshalIndent
utilizzasse quando stampava la nostra struttura come JSON.
Ora abbiamo formattato correttamente il nostro JSON. Tuttavia, notare che i campi per alcuni valori sono stati stampati anche se non abbiamo impostato quei valori. L’encoder JSON può eliminare anche questi campi, se desideri.
Rimozione dei Campi JSON Vuoti
È comune supprimere l’output dei campi non impostati in JSON. Poiché tutti i tipi in Go hanno un “valore zero”, un valore predefinito a cui sono impostati, il pacchetto encoding/json
ha bisogno di informazioni aggiuntive per poter capire che alcuni campi dovrebbero essere considerati non impostati quando si assume questo valore zero. All’interno della parte valore di qualsiasi tag di struttura json
, puoi aggiungere il suffisso del nome desiderato del tuo campo con ,omitempty
per indicare all’encoder JSON di sopprimere l’output di questo campo quando il campo è impostato al valore zero. L’esempio seguente corregge gli esempi precedenti per non stampare più campi vuoti:
Questo esempio produrrà l’output:
Output{
"name": "Sammy the Shark",
"password": "fisharegreat",
"createdAt": "2019-09-23T18:21:53.863846-04:00"
}
Abbiamo modificato gli esempi precedenti in modo che il campo PreferredFish
abbia ora il tag di struttura json:"preferredFish,omitempty"
. La presenza dell’aggiunta ,omitempty
fa sì che l’encoder JSON ignori quel campo, poiché abbiamo deciso di lasciarlo non impostato. Questo aveva il valore null
nei risultati dei nostri esempi precedenti.
Questo output sembra molto meglio, ma stiamo ancora stampando la password dell’utente. Il pacchetto encoding/json
fornisce un altro modo per ignorare completamente i campi privati.
Ignorare i Campi Privati
Alcuni campi devono essere esportati dalle strutture in modo che altri pacchetti possano interagire correttamente con il tipo. Tuttavia, la natura di questi campi può essere sensibile, quindi in queste circostanze, vorremmo che l’encoder JSON ignorasse completamente il campo, anche quando è impostato. Questo viene fatto utilizzando il valore speciale -
come argomento di valore per un tag di struttura json:
.
Questo esempio risolve il problema di esporre la password dell’utente.
Quando esegui questo esempio, vedrai questa uscita:
Output{
"name": "Sammy the Shark",
"createdAt": "2019-09-23T16:08:21.124481-04:00"
}
L’unica cosa che abbiamo cambiato in questo esempio rispetto ai precedenti è che il campo password ora utilizza il valore speciale "-"
per il suo tag di struttura json:
. Nell’output di questo esempio il campo password
non è più presente.
Queste caratteristiche del pacchetto encoding/json
— ,omitempty
, "-"
, e altri opzioni — non sono standard. Quello che un pacchetto decide di fare con i valori di un tag di struttura dipende dalla sua implementazione. Poiché il pacchetto encoding/json
fa parte della libreria standard, altri pacchetti hanno implementato queste caratteristiche allo stesso modo come una questione di convenzione. Tuttavia, è importante leggere la documentazione per qualsiasi pacchetto di terze parti che utilizzi i tag di struttura per scoprire cosa è supportato e cosa non lo è.
Conclusione
Le etichette struct offrono un potente mezzo per potenziare la funzionalità del codice che lavora con le tue strutture. Molte librerie standard e pacchetti di terze parti offrono modi per personalizzare la loro operatività tramite l’uso delle etichette struct. Utilizzarle efficacemente nel tuo codice fornisce sia questo comportamento personalizzato che documenta in modo succinto come questi campi sono utilizzati per i futuri sviluppatori.
Source:
https://www.digitalocean.com/community/tutorials/how-to-use-struct-tags-in-go