/ / Користувальницький JSON серіалізація та десеріалізація для інтерфейсів у Go - json, go, serialization

Спеціальне JSON-серіалізація та десеріалізація для інтерфейсів у Go-json, go, serialization

В даний час я розробляю JSON API для блогу вЯ хочу, щоб у моїх публікаціях містився масив секцій повідомлень, який міг би скласти декілька речей (наприклад, звичайні абзаци, зображення, цитати і т. д.). .). Я використовую Mongo для зберігання (з дивовижним МГО бібліотека), і я хочу зберегти такі публікації:

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

Я спробував кілька рішень, щоб здійснити це в процесі, і ніхто насправді не здавався "правильним шляхом", щоб це зробити:

  1. Не турбуйтеся про вміст

Це здавалося очевидним рішенням, просто використовуючи простий структур:

type PostSection struct{
Type    string
Content interface{}
}

Таким чином, я можу пройти через будь-який інтерфейс POSTS і зберегти його. Проте маніпулювання даними чи перевірка стає неможливим, отже, це не є гарним рішенням.

  1. Використання серіалізації користувальницького інтерфейсу

я знайшов Ця стаття про серіалізацію інтерфейсів в Golang. Спочатку це здавалося чудовим, тому що я міг би мати такий інтерфейс:

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

а потім впровадити кожен тип подібним чином:

type PostImage string

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

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

Оптимально, це було б, і після реалізації MarshalJSON і UnmarshalJSON для всіх моїх типів, він працював добре, коли використовує json.Marshal прямо на об'єкті PostSection.

Однак при серіалізації чи десеріалізації цілий об'єкт Post, що містить масив PostSections, мій спеціальний код просто ігнорувався, і PostSections просто розглядаються як основні об'єкти (string або map[string]string в прикладах) при серіалізації або призводять до порожніх об'єктів при десеріалізації.

  1. Написання користувацької серіалізації для всієї структури Post

Отже, рішення, яке я зараз використовую, але будеЛюбіть змінити - це спеціальна серіалізація для всього об'єкта Post. Це призводить до надзвичайно потворного коду, тому що мені потрібен спеціальний код для одного поля, і тому я проходить через решту, роблячи процедуру десеріалізації подібною до цього:

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...

а потім, розшифруючи такі секції:

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

Це цілий ряд коду, який доведеться використовуватикожен раз, коли я хочу включити PostSections в іншу форму де-небудь інше (наприклад, в інформаційному бюлетені), і це не сприймається як ідіоматичний ідентифікаційний код за допомогою довгого знімка. Також не відбувається обробка помилок для сформованих розділів - вони просто викликають паніку, як це.

Чи існує чисте рішення цієї проблеми?

Відповіді:

2 для відповіді № 1

Щоб уникнути написання UnmarshalJSON для цілого Post ви можете обернути свою PostSection у конкретному типі та реалізувати інтерфейс Unmarshaler.

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
}