Хранение Golang JSON в Postgresql

Я хочу сохранить определенную структуру в моей базе данных, в которой есть поле JSON.

type Comp struct {
    CompId               int64           `db:"comp_id" json:"comp_id"`
    StartDate            time.Time       `db:"start_date" json:"start_date"`
    EndDate              time.Time       `db:"end_date" json:"end_date"`
    WeeklySchedule       json.RawMessage `db:"weekly_schedule" json:"weekly_schedule"`
}

Схема для таблицы:

CREATE TABLE IF NOT EXISTS Tr.Comp(
    comp_id                 SERIAL,
    start_date              timestamp NOT NULL,
    end_date                timestamp NOT NULL,
    weekly_schedule         json NOT NULL,
    PRIMARY KEY (comp_id)
);

Я использую драйвер sqlx и lib/pq в своем проекте, и следующее не будет выполнено. Вместо этого паника говорит, что есть нулевой указатель. DB является глобальной структурой *sqlx.DB struct

    tx := DB.MustBegin()

    compFixture := Comp{
        StartDate:            time.Now(),
        EndDate:              time.Now().AddDate(1, 0, 0),
        WeeklySchedule:       json.RawMessage([]byte("{}")),
    }
    _, err = tx.NamedExec(
        `INSERT INTO 
            Tr.Comp(comp_id, 
                start_date, end_date, weekly_schedule) 
            VALUES (DEFAULT, 
                :start_date, :end_date, :weekly_schedule)  
            RETURNING comp_id;`, compFixture)
    if err != nil {
        t.Fatal("Error creating fixture.", err)
    }

Когда я удаляю weekly_schedule из схемы, а все элементы работают нормально. Но по какой-то причине, когда это поле включено, программа паники. Любая идея относительно того, как я должен определить поле weekly_schedule как в моей схеме БД, так и в структуре Go?

Ответ 1

sqlx имеет тип JSONText в github.com/jmoiron/sqlx/types, который будет делать то, что вам нужно

doc для JSONText

Ответ 2

Я не знаю, насколько это чистое решение, но в итоге я создал свой собственный тип данных JSONRaw. Драйвер DB видит его как []btye, но его все равно можно рассматривать как json.RawMessage в коде Go.

type JSONRaw json.RawMessage

func (j JSONRaw) Value() (driver.Value, error) {
    byteArr := []byte(j)

    return driver.Value(byteArr), nil
}

func (j *JSONRaw) Scan(src interface{}) error {
    asBytes, ok := src.([]byte)
    if !ok {
        return error(errors.New("Scan source was not []bytes"))
    }
    err := json.Unmarshal(asBytes, &j)
    if err != nil {
        return error(errors.New("Scan could not unmarshal to []string"))
    }

    return nil
}

func (m *JSONRaw) MarshalJSON() ([]byte, error) {
    return *m, nil
}

func (m *JSONRaw) UnmarshalJSON(data []byte) error {
    if m == nil {
        return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
    }
    *m = append((*m)[0:0], data...)
    return nil
}

Это повторная реализация копирования MarshalJSON и UnmarshalJSON из библиотеки encoding/json.

Ответ 3

из документации go:

json.RawMessage is a raw encoded JSON object. It implements Marshaler and Unmarshaler and can be used to delay JSON decoding or precompute a JSON encoding.

если вы ввели log.Printf( "% #", цвета) в примере, представленном в пакете json json.RawMessage, вы можете видеть, что после развязывания json-объекта "Point'-member" не отменяется, но остается в [] байте до тех пор, пока цветной формат не будет зафиксирован, а "Точка" явно неармирована.

Вы пытались что-то вроде unmarshal WeeklySchedule перед тем, как поместить его в БД?