JSON in Go umwandeln
Dieses Tool erzeugt aus einem Beispieldokument in Ihrem Browser Structs. Jedes Feld ist exportiert und trägt ein explizites json-Tag, denn encoding/json ignoriert nicht exportierte Felder stillschweigend – ein kleingeschriebener Feldname ist mit Abstand der häufigste Grund, warum ein generierter Struct beim Marshalling ein leeres Objekt liefert.
Das Dokument endet zu früh – es scheint abgeschnitten.
^
Wie funktioniert JSON in Go umwandeln?
Drei Entscheidungen machen einen generierten Go-Struct entweder korrekt oder stillschweigend kaputt, und alle drei betreffen das, was encoding/json zur Laufzeit tut – nicht das, wie der Code aussieht.
Jedes Feld ist exportiert und getaggt
Go marschallt nur exportierte Felder. Ein Struct mit einem Feld namens id liefert bei json.Marshal {} und lässt id nach json.Unmarshal auf zero stehen – in beiden Fällen ohne Fehlermeldung. Deshalb wird jeder Name großgeschrieben und bekommt ein explizites json:"…"-Tag mit dem ursprünglichen Schlüssel. Initialismen folgen dem Style Guide von Go selbst: user_id wird zu UserID, nicht UserId, und api_url wird zu APIURL.
Optionale Felder sind Pointer
Ein einfacher string kann JSON null nicht aufnehmen, und er kann einen fehlenden Schlüssel nicht von einem leeren unterscheiden – beides kommt als "" an. Ein einfaches int kann nicht unterscheiden, ob ein count fehlt oder den Wert 0 hat. Optionale und nullable Felder werden deshalb zu Pointern – der einzigen Konstruktion in Go, die diese Unterscheidung erhält. Slices, Maps und interface{} sind bereits nil-fähig und bleiben deshalb unverändert, statt eine sinnlose zweite Indirektion zu bekommen.
Ganzzahlen, die int nicht fassen kann, werden zu int64
int ist auf manchen Plattformen 32 Bit. Eine Snowflake-ID oder ein Millisekunden-Zeitstempel lässt es überlaufen, und der entstehende Bug taucht nur auf der Plattform auf, auf der der Typ schmal ist. Werte jenseits von 2,147,483,647 werden als int64 ausgegeben.
Unions werden zu interface{}, weil Go keinen Sum Type hat
Ein Array, das sowohl Strings als auch Zahlen enthält, hat keinen Go-Typ, der es beschreibt. Generatoren, die einen erfinden, produzieren Code, der beim Unmarshalling scheitert; interface{} ist die ehrliche Antwort, und der richtige Ort, das zu reparieren, ist die API, nicht der Struct.
Die Ausgabe ist bereits gofmt-ausgerichtet
Namen, Typen, Tags und nachgestellte Kommentare werden auf passende Spalten aufgefüllt. Ohne das erzeugt das erste gofmt in der CI einen Diff an einer Datei, die niemand angefasst hat.
Beispiel
[
{ "user_id": 900719925474099, "api_url": "https://x.dev" },
{ "user_id": 12, "api_url": "https://y.dev", "note": "hi" }
]Generiert
type User struct {
UserID int64 `json:"user_id"`
APIURL string `json:"api_url"`
Note *string `json:"note,omitempty"` // optional
}Welche Optionen und Sonderfälle unterstützt JSON in Go umwandeln?
| Parameter | Typ | Standard | Verhalten & Sonderfälle |
|---|---|---|---|
| Paket | Bezeichner | main | Die Paketklausel am Anfang der Datei. Setzen Sie sie auf das Paket, in dem die Zieldatei liegt – models, api, internal/store. |
| Pointer | Boolesch | on | Optionale und nullfähige Felder werden zu *T, sodass ein fehlender Schlüssel vom Nullwert unterscheidbar ist. Beim Deaktivieren wird die Struct einfacher und fehlende Daten unsichtbar. |
| omitempty | Boolesch | on | Fügt ,omitempty zu optionalen Feldern hinzu, sodass diese bei der Marshalling nicht erscheinen. Beachten Sie, dass omitempty auch eine echte leere Zeichenfolge oder Null entfernt, was genau der Grund ist, warum der Pointer wichtig ist. |
| json.Number | Boolesch | off | Erzeugt json.Number für nicht-ganze Zahlen statt float64 und bewahrt die literalen Ziffern. Sinnvoll für Geldbeträge oder für IDs, die als nicht zitierte Zahlen außerhalb des genauen Bereichs von float64 ankommen. |
| string | Go-Typ | string | Erkannte Formate (uuid, date-time, email, uri) werden in einem Kommentar vermerkt anstatt auf einen Typ gemappt zu werden, da encoding/json einen Zeitstempel nicht in time.Time parsen kann, ohne einen benutzerdefinierten Unmarshaller. |
| integer | Go-Typ | int / int64 | int standardmäßig; int64, wenn irgendein Beispiel 2,147,483,647 übersteigt. Ein gekürzter ID ist ein Bug, der nur im Produktivumfeld auffällt. |
| number | Go-Typ | float64 | Jeder Wert mit einem Dezimalteil. Wenn Ganzzahl und Gleitkommazahl im selben Feld über Datensätze hinweg erscheinen, wird das Feld zu float64. |
| Verschachteltes Objekt | Go-Typ | *Named | Wird zu einer eigenen Struct hochgestuft und per Pointer referenziert, was das Kopieren bei Zuweisung vermeidet und das Feld nil sein lässt. Identische Shapes teilen sich eine Struct. |
Häufig gestellte Fragen
Warum ist jedes Feld großgeschrieben, wenn meine JSON-Schlüssel kleingeschrieben sind?
Weil encoding/json nur exportierte Felder sieht, und Export in Go einen Großbuchstaben am Anfang bedeutet. Ein kleingeschriebenes Feld wird von Marshal und Unmarshal gleichermaßen ohne Fehlermeldung übersprungen – der Struct kompiliert, läuft und tut nichts. Das json-Tag trägt den ursprünglichen Schlüssel, am Wire-Format ändert sich also nichts.
Brauche ich die Pointer?
Nötig sind sie überall dort, wo „fehlend“ und „Null“ nicht dasselbe bedeuten. Ein *int mit dem Wert nil heißt: Der Schlüssel fehlte; ein int mit 0 kann beides bedeuten. Bei einem Feld wie retry_count oder discount kommt es auf diesen Unterschied meist an. Bei einem Feld, das immer vorhanden ist, sind sie nur Rauschen, und die Pointer abzuschalten ist vernünftig.
Warum ist mein Zeitstempel ein string statt time.Time?
Weil encoding/json einen beliebigen Zeitstempel nicht von selbst in time.Time parst – es beherrscht nur RFC 3339, und auch das nur, wenn das Feld als time.Time deklariert ist, was dann bei jedem anderen Format hart scheitert. Aus einem Beispiel generiertes time.Time ergäbe einen Struct, der beim ersten Datensatz, der ein Datum anders formatiert, eine Panic auslöst. Das erkannte Format wird in einem Kommentar vermerkt, damit Sie diese Entscheidung bewusst treffen können.
Was macht interface{} in meinem Struct?
Ein Feld hatte über Ihre Beispiele hinweg wirklich unterschiedliche Typen – in einem Datensatz einen String, in einem anderen ein Objekt –, und Go hat keinen Typ, der das ausdrückt. Die Alternativen sind ein eigenes UnmarshalJSON, ein json.RawMessage, das Sie später dekodieren, oder die API in Ordnung zu bringen. Ein generierter Union-Typ würde beim Unmarshalling schlicht scheitern.
Verändert gofmt die Ausgabe?
Das sollte es nicht. Feldnamen, Typen, Tags und nachgestellte Kommentare werden auf passende Spalten ausgerichtet – genau das macht gofmt mit einem Struct. Falls Sie einen Fall finden, in dem gofmt trotzdem einen Diff erzeugt, ist das ein berichtenswerter Bug und keine Stilfrage.
Wird mein JSON hochgeladen?
Nein. Parsen, Typinferenz und Rendern laufen alle in Ihrem Browser. Nichts wird irgendwohin gesendet – wichtig, denn die Beispiele, die Leute in einen Struct-Generator einfügen, sind meist echte API-Antworten von einem System, gegen das sie gerade integrieren.
Welche verwandten Tools sollten Sie als Nächstes nutzen?
- JSON in CSV umwandelnDatensätze in eine Tabelle umwandeln, Verschachtelung inklusive
- CSV in JSON umwandelnTabellen-Export in Datensätze umwandeln, Anführungszeichen inklusive
- JSON in String umwandelnEin Dokument zum Einbetten als String-Literal maskieren
- JSON-Escapes auflösenEin Dokument aus Maskierungen in einer Logzeile zurückholen