JSON-Persistenzschicht
Teil des Abschnitts Objektorientierte Programmierung der GO-Journey von Coddy. Lektion 102 von 107.
Aufgabe
EinfachFügen wir unserem Bibliotheksverwaltungssystem die Datenpersistenz hinzu! Du wirst eine JSON-Persistenzschicht implementieren, die die Daten deiner Bibliothek zum Speichern in das JSON-Format konvertieren und sie aus JSON-Zeichenfolgen wieder laden kann. Das ist für jede echte Anwendung unerlässlich, die Daten zwischen Sitzungen bewahren muss.
Du organisierst deinen Code über fünf Dateien:
book.go: DeineBook-Struktur mit JSON-Strukturtags zur Steuerung der Serialisierung. Jedes Feld sollte ein entsprechendes JSON-Tag haben:id,title,author,isbnundavailable. Füge deinenNewBook-Konstruktor hinzu.user.go: DeineUser-Struktur mit JSON-Strukturtags fürid,name,emailundborrowed_books. Füge deinenNewUser-Konstruktor hinzu.data.go: Erstelle eineLibraryData-Struktur, die als Transferobjekt für die JSON-Serialisierung dient. Sie sollte zwei Felder haben:Books(ein Slice von Book-Zeigern) undUsers(ein Slice von User-Zeigern), jeweils mit passenden JSON-Tags (booksundusers).library.go: DeineLibrary-Struktur mit Maps für Bücher und Benutzer. Füge zwei neue Methoden hinzu:ToJSON(): Konvertiert die Daten der Bibliothek mithilfe vonjson.MarshalIndentmit einer Einrückung von zwei Leerzeichen in eine formatierte JSON-Zeichenfolge. Sie sollte alle Bücher und Benutzer aus den Maps in einerLibraryData-Struktur sammeln und diese anschließend serialisieren.FromJSON(jsonStr string): Nimmt eine JSON-Zeichenfolge entgegen, deserialisiert sie in eineLibraryData-Struktur und füllt die Maps der Bibliothek mit den geladenen Büchern und Benutzern.
main.go: Demonstriere die Persistenzschicht, indem du eine Bibliothek erstellst und sie in JSON konvertierst.Lies die Anzahl der Bücher ein und anschließend für jedes Buch seine ID, seinen Titel, seinen Autor und seine ISBN. Lies danach die Anzahl der Benutzer ein und anschließend für jeden Benutzer dessen ID, Namen und E-Mail-Adresse. Nachdem du die Bibliothek gefüllt hast, rufe
ToJSON()auf und gib die resultierende JSON-Zeichenfolge aus.
Die folgenden Eingaben werden bereitgestellt:
- Anzahl der Bücher, danach die ID, der Titel, der Autor und die ISBN jedes Buchs (jeweils in einer eigenen Zeile)
- Anzahl der Benutzer, danach die ID, der Name und die E-Mail-Adresse jedes Benutzers (jeweils in einer eigenen Zeile)
Zum Beispiel bei folgender Eingabe:
2
B001
The Go Programming Language
Alan Donovan
978-0134190440
B002
Clean Code
Robert Martin
978-0132350884
1
U001
Alice Smith
alice@library.comDeine Ausgabe sollte folgendermaßen aussehen:
{
"books": [
{
"id": "B001",
"title": "The Go Programming Language",
"author": "Alan Donovan",
"isbn": "978-0134190440",
"available": true
},
{
"id": "B002",
"title": "Clean Code",
"author": "Robert Martin",
"isbn": "978-0132350884",
"available": true
}
],
"users": [
{
"id": "U001",
"name": "Alice Smith",
"email": "alice@library.com",
"borrowed_books": []
}
]
}Und bei folgender Eingabe:
1
B100
Design Patterns
Gang of Four
978-0201633610
0Deine Ausgabe sollte folgendermaßen aussehen:
{
"books": [
{
"id": "B100",
"title": "Design Patterns",
"author": "Gang of Four",
"isbn": "978-0201633610",
"available": true
}
],
"users": []
}Wichtig: Wenn du über Maps iterierst, um deine Slices zu erstellen, füge die Bücher in der Reihenfolge B001, B002 usw. hinzu (alphabetisch nach ID sortiert) und verfahre bei den Benutzern genauso. Dadurch wird eine konsistente JSON-Ausgabe sichergestellt. Du kannst sort.Strings für die Map-Schlüssel verwenden, bevor du iterierst.
Die LibraryData-Struktur überbrückt die Lücke zwischen deiner Map-basierten Speicherung und dem array-basierten Format von JSON. Dieses Transferobjektmuster ist bei der Serialisierung komplexer Datenstrukturen üblich!
Probier es selbst
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Erstelle eine neue Bibliothek
library := NewLibrary()
// Lies die Anzahl der Bücher
numBooksStr, _ := reader.ReadString('\n')
numBooks, _ := strconv.Atoi(strings.TrimSpace(numBooksStr))
// TODO: Lies die Details jedes Buches (ID, title, author, ISBN)
// und füge sie der Bibliothek hinzu
for i := 0; i < numBooks; i++ {
// TODO: Lies die Buch-ID
// TODO: Lies den Buchtitel
// TODO: Lies den Buchautor
// TODO: Lies die Buch-ISBN
// TODO: Erstelle ein Buch und füge es der Bibliothek hinzu
}
// Lies die Anzahl der Benutzer
numUsersStr, _ := reader.ReadString('\n')
numUsers, _ := strconv.Atoi(strings.TrimSpace(numUsersStr))
// TODO: Lies die Details jedes Benutzers (ID, name, email)
// und füge sie der Bibliothek hinzu
for i := 0; i < numUsers; i++ {
// TODO: Lies die Benutzer-ID
// TODO: Benutzernamen lesen
// TODO: Benutzer-E-Mail lesen
// TODO: Benutzer erstellen und zur Bibliothek hinzufügen
}
// TODO: Bibliothek in JSON konvertieren und ausgeben
fmt.Println(library.ToJSON())
}
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 – Dateiparser3Zeiger & 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 Pool12Fortgeschrittene OOP-Konzepte
Functional-Options-PatternBuilder-Pattern in GoMethod ChainingTyp-Aliase vs. DefinitionenGrundlagen der ReflectionÜberblick über Codegenerierung15Projekt: Bibliotheksverwaltung
Projektübersicht & StrukturBuch- und Benutzer-StructsÜbe selbstständig: Online-Go-Compiler