Einführung
Strukturen oder structs werden verwendet, um mehrere Informationen in einer Einheit zusammenzufassen. Diese Informationskollektionen werden verwendet, um Konzepte auf höherer Ebene zu beschreiben, wie z.B. eine Adresse
, die aus einer Straße
, einer Stadt
, einem Bundesstaat
und einem Postleitzahl
besteht. Wenn Sie diese Informationen aus Systemen wie Datenbanken oder APIs lesen, können Sie Struktur-Tags verwenden, um zu steuern, wie diese Informationen den Feldern einer Struktur zugewiesen werden. Struktur-Tags sind kleine Stücke von Metadaten, die an Felder einer Struktur angehängt sind und Anweisungen für anderen Go-Code bereitstellen, der mit der Struktur arbeitet.
Wie sieht ein Struktur-Tag aus?
Go-Struktur-Tags sind Annotationen, die nach dem Typ in einer Go-Strukturdeklaration erscheinen. Jedes Tag besteht aus kurzen Zeichenfolgen, die mit einem entsprechenden Wert verknüpft sind.
A struct tag looks like this, with the tag offset with backtick `
characters:
Andere Go-Code ist dann in der Lage, diese Strukturen zu untersuchen und die Werte abzurufen, die spezifischen angeforderten Schlüsseln zugewiesen sind. Struktur-Tags haben keine Auswirkungen auf die Funktion Ihres Codes ohne zusätzlichen Code, der sie untersucht.
Versuchen Sie dieses Beispiel, um zu sehen, wie Strukturtags aussehen, und dass sie ohne Code aus einem anderen Paket keine Wirkung haben werden.
Dies wird ausgegeben:
OutputHi! My name is Sammy
Dieses Beispiel definiert einen User
-Typ mit einem Name
-Feld. Dem Name
-Feld wurde ein Strukturtag von example:"name"
zugewiesen. Wir würden in einem Gespräch auf diesen spezifischen Tag als „Beispiel-Strukturtag“ verweisen, weil er das Wort „Beispiel“ als Schlüssel verwendet. Der example
-Strukturtag hat den Wert "name"
für das Name
-Feld. Auf dem User
-Typ definieren wir auch die String()
-Methode, die vom fmt.Stringer
-Interface benötigt wird. Diese wird automatisch aufgerufen, wenn wir den Typ an fmt.Println
übergeben, und gibt uns die Möglichkeit, eine schön formatierte Version unserer Struktur zu erzeugen.
Innerhalb des Körpers von main
erstellen wir eine neue Instanz unseres User
-Typs und übergeben ihn an fmt.Println
. Auch wenn die Struktur einen Strukturtag hatte, sehen wir, dass er keine Auswirkung auf die Funktion dieses Go-Codes hat. Es wird sich genau gleich verhalten, wenn der Strukturtag nicht vorhanden wäre.
Um Strukturtag zu verwenden, um etwas zu erreichen, muss anderer Go-Code geschrieben werden, um Strukturen zur Laufzeit zu untersuchen. Die Standardbibliothek enthält Pakete, die Strukturtag als Teil ihrer Operation verwenden. Das beliebteste davon ist das encoding/json
-Paket.
JSON-Codierung
JavaScript Object Notation (JSON) ist ein textuelles Format zur Codierung von Datensammlungen, die unter verschiedenen Zeichenfolgenschlüsseln organisiert sind. Es wird häufig verwendet, um Daten zwischen verschiedenen Programmen zu kommunizieren, da das Format einfach genug ist und Bibliotheken existieren, um es in vielen verschiedenen Sprachen zu decodieren. Das Folgende ist ein Beispiel für JSON:
{
"language": "Go",
"mascot": "Gopher"
}
Dieses JSON-Objekt enthält zwei Schlüssel, language
und mascot
. Nach diesen Schlüsseln folgen die zugehörigen Werte. Hier hat der Schlüssel language
den Wert Go
und mascot
ist mit dem Wert Gopher
belegt.
Der JSON-Encoder in der Standardbibliothek verwendet Strukturtags als Annotationen, die dem Encoder anzeigen, wie Sie Ihre Felder im JSON-Ausgabegerät benennen möchten. Diese JSON-Codierungs- und Decodierungsmechanismen finden sich im encoding/json
Paket.
Versuchen Sie dieses Beispiel, um zu sehen, wie JSON ohne Strukturtags codiert wird:
Dies wird die folgende Ausgabe drucken:
Output{
"Name": "Sammy the Shark",
"Password": "fisharegreat",
"CreatedAt": "2019-09-23T15:50:01.203059-04:00"
}
Wir haben eine Struktur definiert, die einen Benutzer beschreibt, mit Feldern einschließlich ihres Namens, Passworts und der Zeit, zu der der Benutzer erstellt wurde. Innerhalb der main
-Funktion erstellen wir eine Instanz dieses Benutzers, indem wir Werte für alle Felder außer PreferredFish
angeben (Sammy mag alle Fische). Anschließend haben wir die Instanz von User
an die Funktion json.MarshalIndent
übergeben. Dies wird verwendet, damit wir die JSON-Ausgabe einfacher sehen können, ohne ein externes Formatierungstool zu verwenden. Dieser Aufruf könnte durch json.Marshal(u)
ersetzt werden, um JSON ohne zusätzliche Leerzeichen zu drucken. Die beiden zusätzlichen Argumente für json.MarshalIndent
steuern das Präfix für die Ausgabe (das wir mit dem leeren String weggelassen haben) und die Zeichen, die für die Einrückung verwendet werden sollen, die hier zwei Leerzeichen sind. Alle Fehler, die von json.MarshalIndent
erzeugt werden, werden protokolliert, und das Programm wird mit os.Exit(1)
beendet. Schließlich haben wir das von json.MarshalIndent
zurückgegebene []byte
in eine string
umgewandelt und die resultierende Zeichenkette an fmt.Println
übergeben, um sie auf dem Terminal zu drucken.
Die Felder der Struktur erscheinen genau so, wie sie benannt sind. Dies entspricht jedoch nicht dem typischen JSON-Stil, den Sie erwarten würden, der für die Feldnamen Kamelkasten verwendet. Sie werden die Namen der Felder in diesem nächsten Beispiel in Kamelkasten-Stil ändern. Wie Sie sehen werden, wenn Sie dieses Beispiel ausführen, wird dies nicht funktionieren, weil die gewünschten Feldnamen mit den Regeln von Go für exportierte Feldnamen in Konflikt stehen.
Dies ergibt die folgende Ausgabe:
Output{}
In dieser Version haben wir die Namen der Felder geändert, um sie in Camel Case zu benennen. Jetzt ist Name
name
, Password
ist password
, und schließlich CreatedAt
ist createdAt
. Im Körper von main
haben wir die Instanziierung unseres Strukturs geändert, um diese neuen Namen zu verwenden. Wir übergeben dann den Struktur an die Funktion json.MarshalIndent
wie zuvor. Die Ausgabe ist dieses Mal ein leeres JSON-Objekt, {}
.
Das korrekte Benennen von Feldern in Camel Case erfordert, dass der erste Buchstabe kleingeschrieben wird. Während JSON nicht darauf achtet, wie Sie Ihre Felder benennen, tut Go dies, da es die Sichtbarkeit des Feldes außerhalb des Pakets angibt. Da das Paket encoding/json
ein separates Paket vom Paket main
ist, das wir verwenden, müssen wir den ersten Buchstaben großschreiben, um ihn für encoding/json
sichtbar zu machen. Es scheint, dass wir an einem Dilemma stehen. Wir brauchen eine Möglichkeit, dem JSON-Encoder mitzuteilen, wie wir dieses Feld benennen möchten.
Verwenden von Struktur-Tags zur Steuerung der Codierung
Sie können das vorherige Beispiel ändern, um exportierte Felder zu haben, die ordnungsgemäß mit camel-cased Feldnamen codiert sind, indem Sie jedes Feld mit einem Strukturtag annotieren. Der Strukturtag, den encoding/json
erkennt, hat einen Schlüssel von json
und einen Wert, der die Ausgabe steuert. Indem Sie die camel-cased Version der Feldnamen als Wert für den Schlüssel json
platzieren, wird der Encoder diesen Namen verwenden. Dieses Beispiel behebt die beiden vorherigen Versuche:
Dies wird ausgegeben:
Output{
"name": "Sammy the Shark",
"password": "fisharegreat",
"preferredFish": null,
"createdAt": "2019-09-23T18:16:17.57739-04:00"
}
Wir haben die Strukturfelder wieder sichtbar gemacht für andere Pakete, indem wir die ersten Buchstaben ihrer Namen großgeschrieben haben. Dieses Mal haben wir jedoch Struktur-Tags in Form von json:"name"
hinzugefügt, wobei "name"
der Name war, den wir wollten, dass json.MarshalIndent
verwendet, um unsere Struktur als JSON zu drucken.
Wir haben jetzt erfolgreich unser JSON korrekt formatiert. Beachten Sie jedoch, dass die Felder für einige Werte gedruckt wurden, obwohl wir diese Werte nicht gesetzt haben. Der JSON-Encoder kann diese Felder ebenfalls eliminieren, wenn Sie möchten.
Entfernen von leeren JSON-Feldern
Es ist üblich, das Ausgeben von Feldern zu unterdrücken, die in JSON nicht gesetzt sind. Da alle Typen in Go einen „Nullwert“ haben, irgendeinen Standardwert, zu dem sie gesetzt sind, benötigt das Paket encoding/json
zusätzliche Informationen, um festzustellen, dass ein Feld als nicht gesetzt betrachtet werden soll, wenn es diesen Nullwert annimmt. Im Wertteil eines json
-Struktur-Tags können Sie dem gewünschten Namen Ihres Feldes das Suffix ,omitempty
hinzufügen, um den JSON-Encoder anzuweisen, die Ausgabe dieses Feldes zu unterdrücken, wenn das Feld auf den Nullwert gesetzt ist. Das folgende Beispiel korrigiert die vorherigen Beispiele, um keine leeren Felder mehr auszugeben:
Dieses Beispiel wird folgendes ausgeben:
Output{
"name": "Sammy the Shark",
"password": "fisharegreat",
"createdAt": "2019-09-23T18:21:53.863846-04:00"
}
Wir haben die vorherigen Beispiele so geändert, dass das Feld PreferredFish
jetzt das Strukturtag json:"preferredFish,omitempty"
hat. Das Vorhandensein der ,omitempty
-Ergänzung bewirkt, dass der JSON-Encoder dieses Feld überspringt, da wir beschlossen haben, es nicht zu setzen. In den Ausgaben unserer vorherigen Beispiele hatte dies den Wert null
.
Diese Ausgabe sieht schon viel besser aus, aber wir geben immer noch das Passwort des Benutzers aus. Das Paket encoding/json
bietet uns eine weitere Möglichkeit, private Felder vollständig zu ignorieren.
Ignorieren von privaten Feldern
Einige Felder müssen aus Strukturen exportiert werden, damit andere Pakete korrekt mit dem Typ interagieren können. Die Natur dieser Felder kann jedoch sensibel sein, daher möchten wir in diesen Fällen, dass der JSON-Encoder das Feld vollständig ignoriert – auch wenn es gesetzt ist. Dies wird durch die Verwendung des speziellen Werts -
als Wertargument für ein json:
Struktur-Tag erreicht.
Dieses Beispiel behebt das Problem der Offenlegung des Benutzerpassworts.
Wenn Sie dieses Beispiel ausführen, sehen Sie diese Ausgabe:
Output{
"name": "Sammy the Shark",
"createdAt": "2019-09-23T16:08:21.124481-04:00"
}
Das Einzige, was wir in diesem Beispiel gegenüber den vorherigen geändert haben, ist, dass das Passwortfeld jetzt den speziellen Wert "-"
für sein json:
Struktur-Tag verwendet. In der Ausgabe dieses Beispiels ist das password
-Feld nicht mehr vorhanden.
Diese Funktionen des encoding/json
-Pakets – ,omitempty
, "-"
und andere Optionen – sind keine Standards. Was ein Paket mit Werten eines Struktur-Tags macht, hängt von seiner Implementierung ab. Da das encoding/json
-Paket Teil der Standardbibliothek ist, haben auch andere Pakete diese Funktionen konventionell auf die gleiche Weise implementiert. Es ist jedoch wichtig, die Dokumentation für jedes Drittanbieterpaket zu lesen, das Struktur-Tags verwendet, um zu erfahren, was unterstützt wird und was nicht.
Schlussfolgerung
Struct-Tags bieten eine leistungsstarke Möglichkeit, die Funktionalität von Code zu erweitern, der mit Ihren Strukturen arbeitet. Viele Standardbibliotheks- und Drittanbieterpakete bieten Möglichkeiten, ihre Funktionsweise durch die Verwendung von Struktur-Tags anzupassen. Ihre effektive Nutzung in Ihrem Code bietet sowohl dieses Anpassungsverhalten als auch eine prägnante Dokumentation darüber, wie diese Felder von zukünftigen Entwicklern verwendet werden.
Source:
https://www.digitalocean.com/community/tutorials/how-to-use-struct-tags-in-go