JSON поля из sqlite3 в Голанге

У меня есть поле JSON в коллекции sqlite3. Моя схема выглядит так:

CREATE Table Animals(
  id int,
  sounds json,
  name string
)

Я понимаю, что интерфейс go-sqlite не поддерживает явно тип данных json. Однако мой json довольно прост, поскольку все поля являются массивами json, например;

["bark", "woof", "growl"]
["meow", "hiss", "growl"]

Таким образом, полная запись может быть:

id   sounds                      name
1    ["bark", "woof", "growl"]   Fido
2    ["meow", "hiss", "growl"]   Rufus

Используя пакет:

_ "github.com/mattn/go-sqlite3"

Я могу извлечь свое поле JSON с

var id sql.NullInt64
var name sql.NullString
var sounds []uint8

err := db.QueryRow("SELECT id,name,sounds FROM Animals WHERE id = ?;", 1).Scan(&id, &name, &sounds)

fmt.Println(strconv.Itoa(id) + "|" + name + "|" + strings.Join(sounds, "+"))

// does print correctly:
1|Fido|bark+wood+growl

То есть, кажется, что sqlite3 json хранится в строке юникода (?) В виде серии... байтов?..., которую я могу преобразовать в строку с помощью модуля String. Кроме того, мне интересна операция соединения "+", чтобы я мог сделать из этого запрос + строку + поиск + для другого приложения ниже по течению.

Однако мне бы очень хотелось объединить все это в JSON и использовать преимущества разборчивости / разбора JSON, а не специальные пользовательские распечатки. Когда я пытаюсь:

type Animal struct {
    id int                   `json:"id"`
    name sql.NullString      `json:"name"`
    sounds []uint8           `json:"sounds"`
}

var a Animal

err := db.QueryRow("SELECT id,name,sounds FROM Animals WHERE id = ?;", 1).Scan(&a.id, &a.name, &a.sounds
)

Он печатает истинный массив целых чисел. Как я могу встроить объявление strings.Join(sounds []uint8) + преобразование функции в мое определение типа с поддержкой json?

Кроме того, мне не ясно, как использовать строку [] uint8 в том случае, если json равен nulled [] или true NULL, и дополнительно сделать ее устойчивой к ним.

Некоторые ссылки:

1 ответ

Решение

Ваш вопрос поднимает несколько тем. Но самый простой ответ на все из них:

Не используйте реляционную базу данных.

Похоже, вы хотите получать объекты / документы, поэтому использование механизма хранения, который изначально поддерживает это, избавит вас от необходимости использовать ключи везде. MongoDB, CouchDB или какое-либо другое решение NoSQL, вероятно, подходит для ваших потребностей.

Но, сказав это, есть ответы на ваши конкретные вопросы. Вместе взятые, они, возможно, делают что-то сложное и уродливое, хотя.

  1. Ваш sounds тип.

    Создайте пользовательский тип, который реализует интерфейс sql.Scanner и отменяет маршализацию значения JSON для вас:

    type Sounds []string
    
    func (s *Sounds) Scan(src interface{}) error {
        switch t := src.(type) {
        case []byte:
            return json.Unmarshal(t, &s)
        default:
            return errors.New("Invalid type")
        }
    }
    
  2. Сканирование в структуру

    Используйте sqlx для этого. Это позволяет вам сканировать всю строку в структуру гораздо проще, чем стандартная библиотека. Он может использовать db тег для сопоставления строк с полем структуры.

  3. Единая структура для БД и JSON

    Вы можете иметь несколько тегов в своей структуре:

    type Animal struct {
        id int                   `db:"id" json:"id"`
        name sql.NullString      `db:"name" json:"name"`
        sounds []uint8           `db:"sounds" json:"sounds"`
    }
    
Другие вопросы по тегам