encoding/json mit Structs
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 76 von 107.
Das Paket encoding/json ermöglicht es dir, Go-Strukturen in JSON umzuwandeln und zurück. Das ist für APIs, Konfigurationsdateien und die Datenspeicherung unerlässlich. Struktur-Tags steuern, wie Felder JSON-Schlüsseln zugeordnet werden.
Um eine Struktur in JSON zu konvertieren, verwende json.Marshal:
type User struct {
Name string `json:"name"`
Email string `json:"email"`
Age int `json:"age"`
}
func main() {
user := User{Name: "Alice", Email: "alice@example.com", Age: 30}
data, _ := json.Marshal(user)
fmt.Println(string(data))
// {"name":"Alice","email":"alice@example.com","age":30}
}Struct-Tags wie `json:"name"` geben die JSON-Schlüsselnamen an. Ohne Tags verwendet Go direkt den Feldnamen. Nur exportierte Felder (mit Großbuchstaben) werden in die JSON-Ausgabe aufgenommen.
Um JSON wieder in eine Struktur umzuwandeln, verwende json.Unmarshal:
jsonStr := `{"name":"Bob","email":"bob@example.com","age":25}`
var user User
json.Unmarshal([]byte(jsonStr), &user)
fmt.Println(user.Name) // BobZu den gängigen Tag-Optionen gehören omitempty, um Nullwerte zu überspringen, und -, um ein Feld vollständig zu ignorieren:
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Price float64 `json:"price,omitempty"`
Internal string `json:"-"`
}Mit omitempty wird Price, wenn es null ist, nicht im JSON erscheinen. Das Feld Internal wird unabhängig von seinem Wert niemals einbezogen.
Aufgabe
EinfachErstellen wir ein Filmkatalogsystem, das zwischen Go-Strukturen und JSON konvertiert! Du übst, Strukturen in JSON zu marshallen und JSON zurück in Strukturen zu unmarshallen, wobei du Struct-Tags verwendest, um die JSON-Feldnamen zu steuern.
Du organisierst deinen Code auf zwei Dateien verteilt:
movie.go: Definiere dein Filmdatenmodell mit den passenden JSON-Tags.Erstelle eine
Movie-Struktur mit den folgenden Feldern und JSON-Zuordnungen:Title(string) wird"title"zugeordnetDirector(string) wird"director"zugeordnetYear(int) wird"year"zugeordnetRating(float64) wird"rating"mitomitemptyzugeordnet, damit Filme ohne Bewertung kein Bewertungsfeld anzeigenInternalID(string) soll mithilfe von"-"vollständig aus JSON ausgeschlossen werden
Erstelle eine Funktion
ToJSON(m Movie) string, die ein Movie in einen JSON-String konvertiert. Erstelle außerdemFromJSON(jsonStr string) Movie, die einen JSON-String zurück in eine Movie-Struktur parst.main.go: Demonstriere die JSON-Konvertierung in beide Richtungen.Lies einen Operationstyp (
encodeoderdecode) ein.Bei
encode: Lies die Filmdetails (Titel, Regisseur, Jahr, Bewertung) in separaten Zeilen ein. Erstelle ein Movie mit einer InternalID von"internal-123", konvertiere es in JSON und gib das Ergebnis aus.Bei
decode: Lies einen JSON-String ein, konvertiere ihn in eine Movie-Struktur und gib die Filmdetails in diesem Format aus:Title: [title] Director: [director] Year: [year] Rating: [rating]Gib die Bewertung mit einer Nachkommastelle aus. Wenn die Bewertung 0 ist (nicht in JSON vorhanden), gib stattdessen
Rating: N/Aaus.
Die folgenden Eingaben werden bereitgestellt:
- Zeile 1: Operationstyp (
encodeoderdecode) - Für encode: Titel, Regisseur, Jahr, Bewertung (vier Zeilen)
- Für decode: ein JSON-String (eine Zeile)
Beispiel bei folgender Eingabe:
encode
Inception
Christopher Nolan
2010
8.8Deine Ausgabe sollte sein:
{"title":"Inception","director":"Christopher Nolan","year":2010,"rating":8.8}Beachte, dass InternalID aufgrund des "-"-Tags nicht in der Ausgabe erscheint.
Und bei folgender Eingabe:
encode
The Matrix
Wachowskis
1999
0Deine Ausgabe sollte sein:
{"title":"The Matrix","director":"Wachowskis","year":1999}Beachte, dass rating ausgelassen wird, weil es null ist und omitempty verwendet.
Und bei folgender Eingabe:
decode
{"title":"Interstellar","director":"Christopher Nolan","year":2014,"rating":8.6}Deine Ausgabe sollte sein:
Title: Interstellar
Director: Christopher Nolan
Year: 2014
Rating: 8.6Und bei folgender Eingabe:
decode
{"title":"Tenet","director":"Christopher Nolan","year":2020}Deine Ausgabe sollte sein:
Title: Tenet
Director: Christopher Nolan
Year: 2020
Rating: N/AProbier es selbst
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Operationstyp lesen
operation, _ := reader.ReadString('\n')
operation = strings.TrimSpace(operation)
if operation == "encode" {
// Filmdetails lesen
title, _ := reader.ReadString('\n')
title = strings.TrimSpace(title)
director, _ := reader.ReadString('\n')
director = strings.TrimSpace(director)
yearStr, _ := reader.ReadString('\n')
yearStr = strings.TrimSpace(yearStr)
year, _ := strconv.Atoi(yearStr)
ratingStr, _ := reader.ReadString('\n')
ratingStr = strings.TrimSpace(ratingStr)
rating, _ := strconv.ParseFloat(ratingStr, 64)
// TODO: Erstelle eine Movie-Struktur mit den gelesenen Werten
// Setze InternalID auf "internal-123"
// Konvertiere mit ToJSON zu JSON und gib das Ergebnis aus
} else if operation == "decode" {
// JSON-String lesen
jsonStr, _ := reader.ReadString('\n')
jsonStr = strings.TrimSpace(jsonStr)
// TODO: Konvertiere JSON mit FromJSON zu Movie
// Gib die Filmdetails im erforderlichen Format aus
// Verwende fmt.Sprintf("%.1f", rating) für die Formatierung der Bewertung
// Wenn rating 0 ist, gib stattdessen "Rating: N/A" aus
}
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP in Go
Externe DateienGo-Workspace & ModulePackages & ImportsExportierte vs. nicht exportierte NamenEinführung in OOP mit GoStructs als KlassenMethoden für Structs definierenPointer- vs. Value-ReceiverStruct-InitialisierungKonstruktorfunktionenRückblick – Einfacher Taschenrechner4Schnittstellen
Einführung in SchnittstellenImplizite ImplementierungSchnittstelle als VertragLeere Schnittstelle (any)TypzusicherungTypwechselZusammensetzung von SchnittstellenStringer- und Error-SchnittstellenRückblick – Formenrechner7Kapselung
Exportierte vs. nicht exportierte FelderKapselung auf PaketebeneGetter- und Setter-MethodenInformationsverbergung in GoRückblick – Studierendendatensätze10Generics (Go 1.18+)
Einführung in GenericsTypparameterTypbeschränkungenGenerische StrukturenWorkaround für generische MethodenZusammenfassung – Generische Sammlung2Typen & Structs im Detail
Grundlegende & zusammengesetzte TypenBenutzerdefinierte TypdefinitionenStruct-TagsAnonyme StructsVerschachtelte StructsNullwerte & StandardwerteRückblick – Kontaktbuch5Komposition statt Vererbung
Warum Go keine Vererbung hatGrundlagen der Struct-EinbettungMethoden-PromotionMehrere Structs einbettenEinbettung vs. AggregationVerbergen eingebetteter MethodenRückblick – Mitarbeiterhierarchie8Fehlerbehandlung & OOP
Das Error-InterfaceBenutzerdefinierte FehlertypenError-Wrapping (fmt.Errorf)Sentinel-Fehlererrors.Is() und errors.As()Panic, Defer und RecoverRückblick – Dateiparser11Standardbibliothek & OOP
io.Reader & io.Writersort.Interfacefmt.Stringer-Interfaceencoding/json mit Structshttp.Handler-InterfaceRückblick – REST-API-Modelle3Zeiger & Speicher
Grundlagen von Zeigern in GoZeiger auf StructsÜbergabe per Wert vs. ReferenzDie Funktion new()Garbage Collection in GoRückblick – Verkettete Liste erstellen6Polymorphismus in Go
Polymorphismus über InterfacesDuck-Typing in GoRegeln zur Interface-ErfüllungPolymorphe SammlungenDependency InjectionZusammenfassung – Zahlungsprozessor9Konkurrenz & OOP
Grundlagen der GoroutinesChannels & KommunikationGepufferte vs. ungepufferte ChannelsSelect-Anweisungsync.Mutex & sync.RWMutexsync.WaitGroupThread-sicheres Struct-DesignRückblick – Worker PoolÜbe selbstständig: Online-Go-Compiler