Ein Struct-Tag ist ein simpler String — ein Backtick-Literal, das direkt hinter einer Feld-Deklaration steht. Für den Compiler ist dieser String fast bedeutungslos: er beeinflusst die Typ-Identität, sonst nichts. Seine Macht entfaltet er erst, wenn eine Library ihn zur Laufzeit über Reflection ausliest und interpretiert. So wird aus einem unscheinbaren Anhängsel die zentrale Schnittstelle zwischen deinem Go-Typ und der Außenwelt: JSON-Serialisierung, XML-Mapping, Datenbank-Spalten, Validierungs-Regeln, ORM-Beziehungen. Dieser Artikel klärt das formale Format aus der Spec, zeigt die Konvention key:"value", geht die wichtigsten JSON-Optionen durch und macht sichtbar, wie reflect.StructTag die Tags entschlüsselt.

Was ist ein Struct-Tag?

Die Go-Spec definiert das Konstrukt im Abschnitt Struct types ausgesprochen sparsam:

A field declaration may be followed by an optional string literal tag, which becomes an attribute for all the fields in the corresponding field declaration. An empty tag string is equivalent to an absent tag. The tags are made visible through a reflection interface and take part in type identity for structs but are otherwise ignored.

Drei Fakten daraus, die zu verinnerlichen sich lohnt:

  • Ein Tag ist ein String-Literal — beliebiger Inhalt erlaubt.
  • Ein Tag ist über Reflection sichtbar und sonst praktisch unsichtbar.
  • Tags sind Teil der Typ-Identität: zwei Structs mit gleichen Feldern, aber unterschiedlichen Tags, sind verschiedene Typen.
Go erstes-tag.go
package main

import "fmt"

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func main() {
    u := User{ID: 1, Name: "Alice", Email: "alice@example.com"}
    fmt.Printf("%+v\n", u)
}
Output
{ID:1 Name:Alice Email:alice@example.com}

Beachte die Backticks: Tag-Strings werden fast immer als raw string literal geschrieben. Das hat einen guten Grund — innerhalb von Backticks dürfen ASCII-Doppelquotes " ohne Escaping vorkommen. Mit normalen Quotes müsste jedes innere " als \" geschrieben werden, was schnell unlesbar wird.

Die Konvention key:"value"

Ein Tag darf einen beliebigen String enthalten, aber niemand schreibt sie wahllos. Die reflect-Doku legt eine Konvention fest, an die sich praktisch das gesamte Ökosystem hält:

By convention, tag strings are a concatenation of optionally space-separated key:"value" pairs.

Mit den Regeln:

  • Keys bestehen aus Nicht-Steuerzeichen, ohne Leerzeichen, ohne ", ohne :.
  • Values stehen in ASCII-Doppelquotes und folgen der Go-String-Literal-Syntax.
  • Mehrere Paare werden durch Leerzeichen getrennt.
Go mehrere-keys.go
package main

// Mehrere Tag-Keys gleichzeitig an einem Feld:
// - json für die API-Serialisierung
// - db für sqlx
// - validate für go-playground/validator
type Product struct {
    ID    int64   `json:"id"            db:"id"             validate:"required"`
    Name  string  `json:"name"          db:"name"           validate:"required,min=1,max=200"`
    Price float64 `json:"price"         db:"price_cents"    validate:"gte=0"`
}

Die Reihenfolge der Keys spielt keine Rolle. Die Library, die einen bestimmten Key sucht, ignoriert alle anderen. Das macht Multi-Library-Setups wie oben unkompliziert: jede Schicht (Transport, Persistenz, Validierung) liest ihren eigenen Key.

Tags sind nur Strings — Bedeutung kommt von außen

Ein häufiges Missverständnis: Tags wären „magisch" für den Compiler. Sie sind es nicht. Schreibe json:"foo" an ein Feld und die Variable verhält sich exakt gleich wie ohne Tag. Erst wenn encoding/json über reflect.TypeOf(...).Field(i).Tag auf den String zugreift, wird er ausgewertet.

Go kein-effekt-ohne-library.go
package main

import "fmt"

type T struct {
    X int `irgendwas:"hallo"          beliebig:"123"`
}

func main() {
    // Der Tag-String existiert — beeinflusst aber nichts am Verhalten.
    t := T{X: 42}
    fmt.Println(t.X)
}
Output
42

Diese Eigenschaft ist die Stärke des Mechanismus: Du kannst dir eigene Tag-Keys ausdenken, wenn du selbst eine Library schreibst, ohne irgendetwas an der Sprache zu ändern. Genau so sind alle bekannten Keys entstanden — json, xml, db, validate, gorm, yaml sind nichts weiter als Konventionen einzelner Pakete.

reflect.StructTag — Tags zur Laufzeit lesen

Der Typ reflect.StructTag ist ein simpler String-Alias mit zwei nützlichen Methoden: Get und Lookup.

Go reflect-tag.go
package main

import (
    "fmt"
    "reflect"
)

type User struct {
    ID    int    `json:"id"          db:"user_id"`
    Name  string `json:"name"        db:"full_name"     validate:"required"`
    Email string `json:"email"       db:"email_address"`
}

func main() {
    t := reflect.TypeOf(User{})

    for i := 0; i < t.NumField(); i++ {
        f := t.Field(i)
        fmt.Printf("Feld %-5s -> json=%-10s db=%-15s validate=%q\n",
            f.Name,
            f.Tag.Get("json"),
            f.Tag.Get("db"),
            f.Tag.Get("validate"),
        )
    }
}
Output
Feld ID    -> json=id         db=user_id         validate=""
Feld Name  -> json=name       db=full_name       validate="required"
Feld Email -> json=email      db=email_address   validate=""

Get(key) gibt den Wert zurück oder "", wenn der Key fehlt — beides ist ununterscheidbar. Wenn du den Unterschied zwischen „Key nicht gesetzt" und „Key gesetzt, aber leerer Wert" brauchst, ist Lookup der richtige Aufruf:

Go reflect-lookup.go
package main

import (
    "fmt"
    "reflect"
)

type S struct {
    F0 string `alias:"feld_0"`
    F1 string `alias:""`
    F2 string // gar kein Tag
}

func main() {
    t := reflect.TypeOf(S{})
    for i := 0; i < t.NumField(); i++ {
        f := t.Field(i)
        if v, ok := f.Tag.Lookup("alias"); ok {
            if v == "" {
                fmt.Printf("%s: (leer, aber gesetzt)\n", f.Name)
            } else {
                fmt.Printf("%s: %q\n", f.Name, v)
            }
        } else {
            fmt.Printf("%s: (nicht gesetzt)\n", f.Name)
        }
    }
}
Output
F0: "feld_0"
F1: (leer, aber gesetzt)
F2: (nicht gesetzt)

Diese Unterscheidung ist im Library-Design entscheidend, sobald ein leerer Wert eine eigene Bedeutung tragen soll (z. B. „explizit anonymisieren" bei alias:"").

JSON im Detail — Name, -, omitempty

Der json-Tag ist der mit Abstand häufigste in der Praxis. Sein Format ist:

Go json-format.go
Field T `json:"name,option1,option2,..."`

Vor dem ersten Komma steht der JSON-Schlüssel, danach folgen Optionen. Beides ist einzeln optional.

Go json-grundformen.go
package main

import (
    "encoding/json"
    "fmt"
)

type Article struct {
    Title    string `json:"title"`              // Schlüssel umbenannt
    Author   string `json:""`                   // leerer Name -> nimmt "Author"
    Internal string `json:"-"`                  // wird komplett weggelassen
    Dash     string `json:"-,"`                 // Feld heißt im JSON tatsächlich "-"
    Default  string                              // ohne Tag -> "Default"
}

func main() {
    a := Article{
        Title:    "Struct-Tags",
        Author:   "mibeon",
        Internal: "secret",
        Dash:     "literally minus",
        Default:  "ok",
    }
    b, _ := json.MarshalIndent(a, "", "  ")
    fmt.Println(string(b))
}
Output
{
  "title": "Struct-Tags",
  "Author": "mibeon",
  "-": "literally minus",
  "Default": "ok"
}

Drei Sonderfälle hier wichtig:

  • json:"-"Feld komplett ignorieren. Wird weder serialisiert noch deserialisiert.
  • json:"-,"Feld heißt im JSON -. Der Trick: das Komma nach - macht aus dem Sonderfall einen normalen Namen.
  • json:""leerer Name zählt als „nimm den Go-Feldnamen unverändert". Selten nützlich.

omitempty, omitzero, string

Drei Optionen, die du bei json häufig brauchst:

omitempty lässt das Feld weg, wenn der Wert leer im klassischen JSON-Sinn ist: false, 0, nil-Pointer, nil-Interface, leerer Array/Slice/Map/String.

omitzero (seit Go 1.24) lässt das Feld weg, wenn der Wert der Zero Value seines Typs ist — oder eine eigene IsZero() bool-Methode true zurückgibt. Das ist mächtiger als omitempty, weil es z. B. time.Time{} korrekt als „leer" erkennt.

string zwingt das JSON-Encoding, einen primitiven Wert als String-eingebettet zu schreiben. Nützlich für JavaScript-Clients, die mit 64-Bit-Integern nicht präzise rechnen können.

Go omitempty-omitzero.go
package main

import (
    "encoding/json"
    "fmt"
    "time"
)

type Event struct {
    ID        int64     `json:"id"`
    Title     string    `json:"title,omitempty"`     // weg, wenn ""
    ViewCount int       `json:"views,omitempty"`     // weg, wenn 0
    CreatedAt time.Time `json:"created_at,omitzero"` // weg, wenn time.Time{}
    BigNumber int64     `json:"big,string"`          // als JSON-String
}

func main() {
    e := Event{ID: 1, BigNumber: 9007199254740993}
    b, _ := json.MarshalIndent(e, "", "  ")
    fmt.Println(string(b))
}
Output
{
  "id": 1,
  "big": "9007199254740993"
}

Beachte: Title, ViewCount und CreatedAt tauchen im Output nicht auf — sie wurden weggelassen. BigNumber erscheint als String im JSON, obwohl der Go-Typ int64 ist.

omitempty vs. omitzero — der wichtige Unterschied

omitempty und omitzero klingen ähnlich, sind aber nicht dasselbe. Der klassische Bug:

Go omitempty-falle.go
package main

import (
    "encoding/json"
    "fmt"
    "time"
)

type WithEmpty struct {
    T time.Time `json:"t,omitempty"`
}

type WithZero struct {
    T time.Time `json:"t,omitzero"`
}

func main() {
    // time.Time{} ist ein Struct — sein Zero Value ist NICHT
    // "empty" im JSON-Sinn (Structs sind nie empty).
    // Nur omitzero erkennt ihn als leer.
    a := WithEmpty{}
    b := WithZero{}

    ba, _ := json.Marshal(a)
    bb, _ := json.Marshal(b)
    fmt.Println("omitempty:", string(ba))
    fmt.Println("omitzero: ", string(bb))
}
Output
omitempty: {"t":"0001-01-01T00:00:00Z"}
omitzero:  {}

omitempty lässt Structs niemals weg — die Definition von „empty" umfasst keinen Struct-Zero-Value. omitzero hingegen prüft explizit den Zero Value (über IsZero(), falls vorhanden, sonst über Vergleich mit dem Null-Wert). Für time.Time-Felder ist omitzero praktisch immer die richtige Wahl.

Beide Optionen lassen sich kombinieren: json:",omitempty,omitzero" lässt das Feld weg, wenn eines von beidem zutrifft.

XML — andere Optionen, gleiches Prinzip

encoding/xml benutzt denselben Tag-Mechanismus, aber mit eigenem Optionsvokabular. Die wichtigsten:

TagWirkung
xml:"name"Element-Name
xml:"name,attr"als Attribut statt Kind-Element
xml:",chardata"als Text-Inhalt des Eltern-Elements
xml:",cdata"als CDATA-Sektion
xml:",innerxml"roh durchreichen, nicht parsen
xml:",comment"als XML-Kommentar
xml:"a>b>c"verschachteltes Element
xml:"-"komplett ignorieren
xml:"name,omitempty"weglassen, wenn leer
Go xml-tags.go
package main

import (
    "encoding/xml"
    "fmt"
)

type Person struct {
    XMLName xml.Name `xml:"person"`
    ID      int      `xml:"id,attr"`
    Name    string   `xml:"name"`
    Bio     string   `xml:",chardata"`
    Note    string   `xml:",comment"`
    Secret  string   `xml:"-"`
}

func main() {
    p := Person{
        ID:     7,
        Name:   "Alice",
        Bio:    "Software-Entwicklerin",
        Note:   "intern erfasst",
        Secret: "wird ignoriert",
    }
    b, _ := xml.MarshalIndent(p, "", "  ")
    fmt.Println(string(b))
}
Output
<person id="7">Software-Entwicklerin<!--intern erfasst--><name>Alice</name></person>

Die Mischung von chardata, attr und benannten Elementen erlaubt erstaunlich kompakte Mappings auf bestehende XML-Schemas — exakt der Use-Case, für den die Optionen entworfen wurden.

Weitere häufige Tag-Keys

Außerhalb der Standard-Library haben sich einige Keys als Quasi-Standards etabliert:

KeyLibraryBeispiel
dbjmoiron/sqlx, sqlcdb:"user_id"
validatego-playground/validatorvalidate:"required,email,max=200"
gormgorm.io/gormgorm:"primaryKey;column:id"
yamlgopkg.in/yaml.v3yaml:"server_address"
tomlBurntSushi/tomltoml:"server_address"
envcaarlos0/envenv:"PORT" envDefault:"8080"
mapstructuremitchellh/mapstructuremapstructure:"server_address"
bsongo.mongodb.org/mongo-driverbson:"user_id"
formgin-gonic/ginform:"q"

Alle folgen derselben Konvention key:"value[,option,…]". Wer eine neue Library entwirft, sollte sich daran halten — Tooling wie go vet prüft das Format und meldet Abweichungen.

go vet warnt vor kaputten Tags

Der structtag-Analyzer von go vet läuft automatisch mit und meckert, wenn ein Tag nicht der key:"value"-Konvention folgt — fehlende Quotes, falsche Trennzeichen, doppelte Keys.

Go vet-warnung.go
type Bad struct {
    // Fehler: kein Space zwischen den Keys, vet meckert.
    A string `json:"a"xml:"a"`

    // Fehler: Value nicht in Quotes.
    B string `json:b`

    // Fehler: doppelter json-Key.
    C string `json:"c" json:"c2"`
}

Ein Lauf von go vet ./... würde Meldungen wie

Plain vet-output
bad.go:3:9: struct field tag `json:"a"xml:"a"` not compatible with reflect.StructTag.Get: bad syntax for struct tag pair
bad.go:6:9: struct field tag `json:b` not compatible with reflect.StructTag.Get: bad syntax for struct tag value
bad.go:9:9: struct field C repeats json tag "c"

ausspucken. In CI-Pipelines ist go vet Pflicht — diese Klasse von Fehlern darf nie ins Production-Code-Repo kommen, weil Tag-Parser sich an kaputten Strings höchst unterschiedlich verhalten.

Praxis 1 — User-Struct mit json, db, validate

Ein realistisches Beispiel aus einer typischen REST-API: derselbe Struct trägt JSON-Schema für Transport, Spalten-Mapping für die Datenbank und Validierungsregeln für den Input-Check.

Go user-multi-tag.go
package main

import (
    "encoding/json"
    "fmt"
    "time"
)

type User struct {
    ID        int64     `json:"id"                  db:"id"             validate:"-"`
    Email     string    `json:"email"               db:"email"          validate:"required,email"`
    Name      string    `json:"name"                db:"full_name"      validate:"required,min=1,max=200"`
    Age       int       `json:"age,omitempty"       db:"age"            validate:"gte=0,lte=130"`
    CreatedAt time.Time `json:"created_at,omitzero" db:"created_at"     validate:"-"`
    Password  string    `json:"-"                   db:"password_hash"  validate:"required,min=8"`
}

func main() {
    u := User{
        ID:        1,
        Email:     "alice@example.com",
        Name:      "Alice",
        CreatedAt: time.Date(2026, 5, 20, 12, 0, 0, 0, time.UTC),
        Password:  "supersecret",
    }
    b, _ := json.MarshalIndent(u, "", "  ")
    fmt.Println(string(b))
}
Output
{
  "id": 1,
  "email": "alice@example.com",
  "name": "Alice",
  "created_at": "2026-05-20T12:00:00Z"
}

Die wichtigen Design-Entscheidungen hier:

  • json:"-" für Password — das Passwort-Hash darf niemals in einer API-Response auftauchen, der db-Mapping bleibt aber erhalten.
  • omitempty bei Age0 wird unterdrückt, weil „Alter 0" praktisch immer „nicht angegeben" heißt.
  • omitzero bei CreatedAt — der Zero-Time wird unterdrückt, nicht der 0001-01-01-Mojibake-String.
  • validate:"-" an Server-Feldern — IDs und Timestamps stammen vom Server, nicht vom Client; sie werden vom Validator übersprungen.

Praxis 2 — API-Versionierung mit getrennten Structs

Eine reife API trennt Wire-Format (was der Client schickt) vom Persistenz-Modell (was in der Datenbank steht). Dasselbe User-Konzept lebt in zwei verschiedenen Structs mit unterschiedlichen Tags — eine Library-übergreifende Vermischung wird so vermieden.

Go api-versionierung.go
package main

import (
    "encoding/json"
    "fmt"
    "time"
)

// CreateUserRequest — kommt vom Client.
// Nur die Felder, die der Client setzen darf. Validate-Regeln streng.
type CreateUserRequest struct {
    Email    string `json:"email"    validate:"required,email"`
    Name     string `json:"name"     validate:"required,min=1,max=200"`
    Password string `json:"password" validate:"required,min=8,max=72"`
}

// User — Persistenz-Modell. Tag-Mix anders, Felder mehr.
type User struct {
    ID           int64     `db:"id"`
    Email        string    `db:"email"`
    Name         string    `db:"full_name"`
    PasswordHash string    `db:"password_hash"`
    CreatedAt    time.Time `db:"created_at"`
    UpdatedAt    time.Time `db:"updated_at"`
}

// UserResponse — geht an den Client zurück.
// Kein Passwort-Feld, sorgfältig kuratiert.
type UserResponse struct {
    ID        int64     `json:"id"`
    Email     string    `json:"email"`
    Name      string    `json:"name"`
    CreatedAt time.Time `json:"created_at"`
}

func toResponse(u User) UserResponse {
    return UserResponse{
        ID: u.ID, Email: u.Email, Name: u.Name, CreatedAt: u.CreatedAt,
    }
}

func main() {
    // Simulation: Client schickt Create-Request
    in := `{"email":"alice@example.com","name":"Alice","password":"s3cret123"}`
    var req CreateUserRequest
    _ = json.Unmarshal([]byte(in), &req)

    // Server baut Persistenz-Modell
    user := User{
        ID: 1, Email: req.Email, Name: req.Name,
        PasswordHash: "<bcrypt-hash>",
        CreatedAt:    time.Date(2026, 5, 20, 12, 0, 0, 0, time.UTC),
    }

    // Antwort an Client (ohne Hash, ohne UpdatedAt)
    out, _ := json.MarshalIndent(toResponse(user), "", "  ")
    fmt.Println(string(out))
}
Output
{
  "id": 1,
  "email": "alice@example.com",
  "name": "Alice",
  "created_at": "2026-05-20T12:00:00Z"
}

Die Trennung wirkt erst überdimensioniert, zahlt sich aber sofort aus, sobald die API Version 2 bekommt: CreateUserRequestV2 kann neue Felder erzwingen, ohne das DB-Modell zu brechen — und umgekehrt kann sich das DB-Modell weiterentwickeln, ohne den Wire-Vertrag zu zerlegen.

Edge — Embedded Fields und unexported Fields

Zwei Sonderfälle, die immer wieder Verwirrung stiften:

Embedded Fields können Tags tragen wie jedes andere Feld. Ohne Tag werden die Felder des eingebetteten Typs „nach oben gezogen" und mit ihren eigenen Tags serialisiert (Promotion). Mit Tag erscheint das eingebettete Struct als verschachteltes JSON-Objekt.

Go embedded-tag.go
package main

import (
    "encoding/json"
    "fmt"
)

type Address struct {
    City string `json:"city"`
    ZIP  string `json:"zip"`
}

type WithoutTag struct {
    Name string `json:"name"`
    Address       // ohne Tag -> Felder werden promoted
}

type WithTag struct {
    Name    string  `json:"name"`
    Address Address `json:"address"` // mit Tag -> als Sub-Objekt
}

func main() {
    a := WithoutTag{Name: "Alice", Address: Address{City: "Berlin", ZIP: "10115"}}
    b := WithTag{Name: "Alice", Address: Address{City: "Berlin", ZIP: "10115"}}

    ba, _ := json.Marshal(a)
    bb, _ := json.Marshal(b)
    fmt.Println("ohne Tag:", string(ba))
    fmt.Println("mit Tag: ", string(bb))
}
Output
ohne Tag: {"name":"Alice","city":"Berlin","zip":"10115"}
mit Tag:  {"name":"Alice","address":{"city":"Berlin","zip":"10115"}}

Unexported Fields (kleingeschrieben) werden vom JSON-Encoder grundsätzlich ignoriert — auch wenn sie einen Tag tragen. Reflection kommt an unexportierte Felder nicht heran, und der encoding/json-Encoder ignoriert sie konsequent.

Go unexported-tag.go
package main

import (
    "encoding/json"
    "fmt"
)

type T struct {
    Public  string `json:"public"`
    private string `json:"private"` // Tag wird ignoriert
}

func main() {
    t := T{Public: "sichtbar", private: "unsichtbar"}
    b, _ := json.Marshal(t)
    fmt.Println(string(b))
}
Output
{"public":"sichtbar"}

Wer ein Feld intern halten, aber serialisieren will, hat zwei Optionen: ein Exported-Feld mit json:"-" und manueller Serialisierung, oder ein eigenes MarshalJSON auf dem Typ. Tags allein helfen hier nicht.

Besonderheiten

Tags sind Teil der Typ-Identität.

Zwei anonyme Structs mit identischen Feldern, aber unterschiedlichen Tags, gelten als verschiedene Typen — Zuweisung ohne Conversion schlägt fehl. Praktische Folge: Wenn du in einem Interface-Test denselben Struct neu deklarierst, aber mit anderen Tags, ist das ein anderer Typ als der „echte". Die Spec sagt das explizit: Tags take part in type identity for structs.

Backticks sind Pflicht für lesbare Tags.

Tag-Strings als normale "..."-Literale schreiben funktioniert technisch — wird aber zur Escape-Hölle, weil jedes innere " als \" notiert werden muss. Raw-Strings in Backticks erlauben ASCII-Doppelquotes ohne Escaping. Daher: immer Backticks.

json:"-" und json:"-," sind verschieden.

Der erste schließt das Feld vollständig aus der JSON-Repräsentation aus. Der zweite — mit Komma — sagt: das Feld heißt im JSON tatsächlich -. Das Komma ist die Unterscheidung. Klassischer Stolperer beim ersten Hinsehen, weil ein einzelnes Zeichen den Sinn invertiert.

omitempty erkennt keine Zero-Structs.

Ein time.Time{} ist nach JSON-Definition nicht empty — Structs sind nie empty. Wer time.Time-Felder konditional weglassen will, braucht entweder einen Pointer (*time.Time) oder Go 1.24+ mit omitzero. omitempty allein schickt den 0001-01-01T00:00:00Z-String mit.

go vet ist der Tag-Linter.

Der eingebaute structtag-Analyzer prüft das Format. Fehlende Quotes, fehlende Leerzeichen, doppelte Keys — alle landen im Vet-Output. CI-Pipelines sollten go vet ./... zwingend ausführen, weil Tag-Parser kaputter Strings nicht alle gleich reagieren.

Unexported Fields ignorieren Tags vollständig.

Reflection kommt über das reflect-Paket nicht an klein geschriebene Felder heran. Der JSON-Encoder, der Validator, der ORM — alle sehen unexportierte Felder gar nicht erst. Ein json:"foo" an einem unexported string ist toter Code.

Embedded ohne Tag promoted Felder, mit Tag verschachtelt.

Ein eingebetteter Typ ohne eigenen Tag bringt seine Felder per Promotion in den JSON-Output auf oberster Ebene. Mit Tag wird er zu einem verschachtelten Sub-Objekt. Beide Varianten sind legitim — welche du willst, hängt vom Wire-Vertrag der API ab. Vorher festlegen, dann konsistent durchziehen.

Tags sind eine offene Konvention.

Es gibt keine zentrale Liste „erlaubter" Tag-Keys. json, xml, db, validate sind Quasi-Standards einzelner Pakete. Wer eine eigene Library schreibt, darf einen eigenen Key wählen — mibeon:"…" ist ebenso legitim wie alles andere. Die einzige Regel: bei der Konvention key:&quot;value&quot; bleiben, damit go vet und reflect.StructTag.Get funktionieren.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Structs & Methoden

Zur Übersicht