/ / Benutzerdefinierte JSON - Serialisierung und Deserialisierung für Schnittstellen in Go - json, go, Serialisierung

Benutzerdefinierte JSON - Serialisierung und Deserialisierung für Schnittstellen in Go - json, go, Serialisierung

Ich entwickle gerade eine JSON API für einen Blog inIch bin in einen Roadblock gerannt, der versucht, die Serialisierung und Deserialisierung von Blogposts zu handhaben. Ich möchte, dass meine Posts eine Reihe von Post Sections enthalten, die eine Reihe von Dingen beinhalten können (wie normale Absätze, Bilder, Zitate usw.) .) Ich benutze Mongo für die Speicherung (mit dem erstaunlichen MBO-Bibliothek) und ich möchte die Beiträge so speichern:

{
"title": "Blog post",
"sections": [
{
"type": "text",
"content": { "en": "English content", "de": "Deutscher Inhalt" }
},
{
"type": "image",
"content": "https://dummyimage.com/100x100"
},
...more sections
],
...other fields
}

Ich habe mehrere Lösungen ausprobiert, um dies umzusetzen, und niemand schien wirklich der richtige Weg dafür zu sein:

  1. Sich nicht um den Inhalt kümmern

Dies schien die offensichtliche Lösung zu sein, nur eine einfache Struktur zu verwenden:

type PostSection struct{
Type    string
Content interface{}
}

Auf diese Weise kann ich die POST-Funktionen des Frontends durchlaufen und speichern. Es ist jedoch unmöglich, die Daten zu manipulieren oder zu validieren. Es ist also keine gute Lösung.

  1. Verwenden der benutzerdefinierten Schnittstellenserialisierung

ich fand Dieser Artikel über das Serialisieren von Schnittstellen in Golang. Das erschien mir zuerst super, weil ich so eine Schnittstelle haben könnte:

type PostSection interface{
Type()    string
Content() interface{}
}

und dann jeden Typ wie folgt implementieren:

type PostImage string

func (p *PostImage) Type() string {
return "image"
}

func (p *PostImage) Content() interface{} {
return p
}

Optimal wäre das gewesen, und nach der Umsetzung MarshalJSON und UnmarshalJSON Für alle meine Typen funktionierte es einwandfrei, wenn Sie json.Marshal direkt auf einem PostSection-Objekt verwendeten.

Beim Serialisieren oder Deserialisieren eines gesamten Post-Objekts mit einem Array von PostSections, mein benutzerdefinierter Code wurde einfach ignoriert und die PostSections würden nur als die zugrunde liegenden Objekte behandelt (string oder map[string]string in den Beispielen) beim Serialisieren oder beim Deserialisieren zu leeren Objekten führen.

  1. Benutzerdefinierte Serialisierung für die gesamte Post-Struktur schreiben

Also, die Lösung, die ich gerade benutze, würde es aber tunDas Ändern ist eine benutzerdefinierte Serialisierung für das gesamte Post-Objekt. Dies führt zu einem super hässlichen Code, da ich nur einen benutzerdefinierten Code für ein einzelnes Feld benötige und so den Rest durchlaufe und die Deserialisierung ähnlich aussehen lässt:

p.ID = decoded.ID
p.Author = decoded.Author
p.Title = decoded.Title
p.Intro = decoded.Intro
p.Slug = decoded.Slug
p.TitleImage = decoded.TitleImage
p.Images = decoded.Images
...more fields...

und dann die Abschnitte wie folgt dekodieren:

sections := make([]PostSection, len(decoded.Sections))
for i, s := range decoded.Sections {
if s["type"] == "text" {
content := s["content"].(map[string]interface{})
langs := make(PostText, len(content))
for lang, langContent := range content {
langString := langContent.(string)
langs[lang] = langString
}
sections[i] = &langs
} else if s["type"] == "image" {
content := s["content"].(string)
contentString := PostImage(content)
sections[i] = &contentString
}
}

p.Sections = sections

Das ist eine Menge Code, den ich benutzen mussJedes Mal, wenn ich PostSections in einer anderen Form an anderer Stelle (zum Beispiel in einem Newsletter) einfügen möchte, fühlt es sich auf lange Sicht nicht wie idiomatischer Code an. Außerdem gibt es keine Fehlerbehandlung für fehlerhafte Abschnitte - Sie verursachen nur eine Panik wie Dies.

Gibt es eine saubere Lösung für dieses Problem?

Antworten:

2 für die Antwort № 1

Um zu vermeiden, zu schreiben UnmarshalJSON für das ganze Post Sie können Ihre verpacken PostSection in einem konkreten Typ und implementieren Sie die Unmarshaler-Schnittstelle.

type Post struct {
ID         int
Author     string
Title      string
Intro      string
Slug       string
TitleImage string
Images     []string

Sections []*PostSection
}

type SectionContent interface {
Type()    string
Content() interface{}
}

type PostSection struct {
Content SectionContent
}

func (s *PostSection) UnmarshalJSON(data []byte) error {
// ...
return nil
}