Неупорядочить массив json в массив структуры
Я использую
Mysql 8
. Я также использую
99designs/gqlgen
для автоматического создания структур на основе схемы GraphQL. Я должен был повторно использовать те же структуры при сканировании ответов MySql. И вдобавок ко всему, во время создания прототипа я хочу, чтобы в моей таблице было несколько JSON. Итак, структура такая:
type CustomizedItemInput struct {
Sku string `json:"sku"`
Name string `json:"name"`
Skus []*CustomizedComponent `json:"skus"`
...
С момента хранения (предоставления
Value()
) проще мне удалось сохранить Skus в БД как JSON верхнего уровня. Выглядит так:
[{"sku": "123", "position": "LEFT"}, {"sku": "456", "position": "RIGHT"}]
Теперь, как мне без особых усилий получить это значение из БД и обратно в массив указателей внутри структуры? Конечно, в идеале это должно быть сделано без изменения базовой структуры, поскольку она создается автоматически.
ОБНОВЛЕНИЕ: добавление отладочной информации. Мне нужно прочитать строку БД в CustomizedItemView, которая в основном отражает CustomizedItemInput сверху:
type CustomizedItemView struct {
Sku string `json:"sku"`
Name string `json:"name"`
Skus []*CustomizedComponentView `json:"skus"`
...
Конечно, когда я говорю "без суеты", я имею в виду беспрепятственное извлечение строки БД в структуру. Я могу добавить
map[string]interface{}{}
со всеми наворотами и получите значение. Но я хочу, чтобы он был аккуратным, например:
var storedCustItem = model.CustomizedItemView{}
err := udb.Get(&storedCustItem, database.SelectCustomizationQuery, userID, custItem.Sku, createdAt)
Я получаю следующую ошибку:
2020/10/10 20:38:24 sql: Scan error on column index 8, name "skus": unsupported Scan, storing driver.Value type []uint8 into type *[]*model.CustomizedComponentView
(8, потому что я удалил некоторые поля для примера). Основная проблема в том, что я не могу создавать
Scan()
для безымянного типа. Я создал обертки для
Value()
потому что мои вставки более подробны, и я делаю преобразование типов с типом оболочки в них:
type CustomizedComponentsIn []*CustomizedComponent
...
func (customizedComponents CustomizedComponentsIn) Value() (driver.Value, error)
...
tx.MustExec(database.SaveCustomizationCommand,
custItem.Sku,
custItem.Name,
model.CustomizedComponentsIn(custItem.Skus)
...
, что подходит для вставок, потому что будут некоторые значения, которые не принадлежат входной структуре. Но я надеялся, по крайней мере, получить автоматическое сканирование значения в структуру представления.
1 ответ
Если вы можете изменить тип
Skus
поле, общий подход заключался бы в объявлении типа среза, который реализует
sql.Scanner
и
driver.Valuer
интерфейсов и используйте их вместо безымянных
[]*CustomizedComponent
тип.
Например:
type CustomizedItemInput struct {
Sku string `json:"sku"`
Name string `json:"name"`
Skus CustomizedComponentSlice `json:"skus"`
// ...
}
type CustomizedComponentSlice []*CustomizedComponent
// Value implements driver.Valuer interface.
func (s CustomizedComponentSlice) Value() (driver.Value, error) {
return json.Marshal(s)
}
// Scan implements sql.Scanner interface.
func (s *CustomizedComponentSlice) Scan(src interface{}) error {
var data []byte
switch v := src.(type) {
case string:
data = []byte(v)
case []byte:
data = v
default:
return nil
}
return json.Unmarshal(data, s)
}
Если вы не можете изменить тип
Skus
field вам нужно будет явно преобразовать поле во время сканирования.
Например, для указанного выше типа среза вы можете сделать что-то вроде этого:
v := new(CustomizedItemView)
row := db.QueryRow("SELECT sku, name, skus FROM customized_item_view WHERE sku = ? LIMIT 1", sku)
err := row.Scan(
&v.Sku,
&v.Name,
// do the conversion here, and any other place where you're scanning Skus...
(*CustomizedComponentSlice)(&v.Skus),
)
if err != nil {
return err
}
fmt.Println(v.Skus) // result