Обновить
5
Карма
0
Рейтинг

Пользователь

  • Подписчики 1
  • Подписки

Динамическое изменение схемы JSON в Go с помощью gob

Здесь обратная задача, то есть, не в json, а обратно. При этом в условиях задано, что структура неизвестная.
map[string]interface{} — это просто общий интерфейс объекта json. Естественно, что в него декодируется любой объект из json строки.
И да, конечно, переменную типа map[string]interface{} можно создать в коде и потом её кодировать в json, и это было бы решением прямой задачи. Собственно, его код я и спрашивал. Просто интересно, чем конкретно оно лучше. Те решения, что я встречал (через map[string]interface{}), лучшими бы я не назвал.

Динамическое изменение схемы JSON в Go с помощью gob

А можно пример кода, как именно предлагается сделать?

Динамическое изменение схемы JSON в Go с помощью gob

Если я правильно понял, то Вы предлагаете использовать дополнительный приватный атрибут типа
toJson           func() (data []byte, err error)

И именно в него положить всю логику преобразования в json.
При этом для каждого отображения нужно иметь свою функцию. Теперь вопрос: а что будет в тех функциях? типа такого:

        return json.Marshal(&struct {
            Id               int64  `json:"ref"`
            Title            string `json:"title"`
            Description      string `json:"description"`
            PromoPrice       int64  `json:"price"`
            PromoDescription string `json:"promo,omitempty"`
            Links            Links  `json:"links"`
        }{
            Id:               b.Id,
            Title:            b.Title,
            Description:      b.Description,
            PromoPrice:       b.PromoPrice,
            PromoDescription: b.PromoDescription,
            Links:            b.Links,
        })

Если как по мне, то читабельности и разделения кода становится на порядок меньше, хотя как показывают замеры скорости, это будет быстрее.
Или имелось в виду совсем другое решение?

Динамическое изменение схемы JSON в Go с помощью gob

Про скорость и бенчмарки. (в ответ TonyLorencio, Sly_tom_cat и, кончено, всем, кому интересно).
Главный вопрос тут, конечно, про целесообразность использования gob, и дополнительного кодирования/декодирования, поэтому давайте рассмотрим именно этот момент.
Вообще, решение конечно более архитектурное, направленное на разделение кода, чтобы при сливании git веток меньше приходилось править конфликты, которые бы были неизбежны, если бы всё решение было внутри функции MarshalJSON.
Итак, давайте сделаем тоже самое, но без использования gob, а с прямым заполнением структуры. В коде есть закомментированный текст — о нём будет далее.
book1.go
package models

import (
    "encoding/json"
    "fmt"
    "time"
)

const (
    SiteBook1View = iota
    Partner1Book1View
    Partner2Book1View
    PromoBook1View
)

func (b *Book1) SetDefaultView() {
    b.SetSiteView()
}

func (b *Book1) SetSiteView() {
    b.view = SiteBook1View
}

func (b *Book1) SetPartner1View() {
    b.view = Partner1Book1View
}

func (b *Book1) SetPartner2View() {
    b.view = Partner2Book1View
}

func (b *Book1) SetPromoView() {
    b.view = PromoBook1View
}

type Book1 struct {
    Id               int64
    Title            string
    Description      string
    Partner2Title    string
    Price            int64
    PromoPrice       int64
    PromoDescription string
    Partner1Price    int64
    Partner2Price    int64
    // Links            Links
    UpdatedAt time.Time
    CreatedAt time.Time
    view      int
}

func (b Book1) MarshalJSON() (data []byte, err error) {
    if b.view == 0 {
        b.SetDefaultView()
    }
    switch b.view {
    case SiteBook1View:
        return json.Marshal(SiteBookView{
            Id:          b.Id,
            Title:       b.Title,
            Description: b.Description,
            Price:       b.Price,
            // Links:       b.Links,
        })
    case Partner1Book1View:
        return json.Marshal(Partner1BookView{
            Id:            b.Id,
            Title:         b.Title,
            Partner1Price: b.Partner1Price,
            // Links:         b.Links,
        })
    case Partner2Book1View:
        return json.Marshal(Partner2BookView{
            Id:            b.Id,
            Partner2Title: b.Partner2Title,
            Description:   b.Description,
            Partner2Price: b.Partner2Price,
            // Links:         b.Links,
        })
    case PromoBook1View:
        return json.Marshal(PromoBookView{
            Id:               b.Id,
            Title:            b.Title,
            Description:      b.Description,
            PromoPrice:       b.PromoPrice,
            PromoDescription: b.PromoDescription,
            // Links:            b.Links,
        })
    default:
        err = fmt.Errorf("undefined view")
        return
    }
    return
}




Тест создаёт список из элементов и потом получает json строку. Результаты:
BenchmarkRun0-8 20000 68701 ns/op
BenchmarkRun1-8 200000 7177 ns/op

То есть, использование gob даёт замедление в 10 раз. Да, это несколько больше, чем ожидалось.
Но это простые типы данных: int64 и string. А что будет, если будет что-то посложнее? Давайте добавим список литературы.
Код LInks
type Link struct {
	Title       string
	Description string
	Rate        float64
}

type Links []Link

type Book struct {
	Id               int64
	Title            string
	Description      string
	Partner2Title    string
	Price            int64
	PromoPrice       int64
	PromoDescription string
	Partner1Price    int64
	Partner2Price    int64
	Links            Links
	UpdatedAt time.Time
	CreatedAt time.Time
	view      BookView
}
type SiteBookView struct {
	Id          int64  `json:"sku"`
	Title       string `json:"title"`
	Description string `json:"description"`
	Price       int64  `json:"price"`
	Links       Links  `json:"links"`
}
//к остальным также добавим Links с каким-то json всевдонимом


Пусть всегда будет по списку литературу в 100 позиций. Результаты уже не столь плачевны:
BenchmarkRun0-8 3000 493611 ns/op
BenchmarkRun1-8 5000 322898 ns/op
Замедление уже составляет 52%. При более сложных данных может быть и меньше.
Если вам нужно выводить до 100 элементов, при этом их структура достаточно сложная, то замедление будет не столь критичным. Если это тонны информации, и каждая миллисекунда на счету, то, возможно, то данное решение может быть не очень эффективным.
Да, это плата за разделение кода, уменьшение риска конфликтов при слиянии git веток, наглядность моделей. Возможно, этого всего можно добиться и без gob, или с ним, но другим, более эффективным решением. Если найду — обязательно поделюсь.

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность