Как я могу эффективно работать с значениями NULL SQL и JSON в Голанге?

Такие типы, как Int64 и String не могут хранить нулевые значения, поэтому я обнаружил, что для этого могу использовать sql.NullInt64 и sql.NullString.

Но когда я использую их в Struct и генерирую JSON из Struct с пакетом json, тогда формат отличается от того, когда я использую обычные типы Int64 и String.

JSON имеет дополнительный уровень, потому что sql.Null *** также является Struct.

Есть ли хороший способ для этого, или я не должен использовать NULL в моей базе данных SQL?

Ответ 1

Такие типы, как sql.NullInt64, не выполняют никакой специальной обработки для JSON-маршалинга или размонтирования, поэтому применяются правила по умолчанию. Так как тип является структурой, он получает сортировку как объект с полями в качестве атрибутов.

Один из способов обойти это - создать свой собственный тип, который реализует интерфейсы json.Marshaller/json.Unmarshaler. Встраивая тип sql.NullInt64, мы получаем бесплатные методы SQL. Что-то вроде этого:

type JsonNullInt64 struct {
    sql.NullInt64
}

func (v JsonNullInt64) MarshalJSON() ([]byte, error) {
    if v.Valid {
        return json.Marshal(v.Int64)
    } else {
        return json.Marshal(nil)
    }
}

func (v *JsonNullInt64) UnmarshalJSON(data []byte) error {
    // Unmarshalling into a pointer will let us detect null
    var x *int64
    if err := json.Unmarshal(data, &x); err != nil {
        return err
    }
    if x != nil {
        v.Valid = true
        v.Int64 = *x
    } else {
        v.Valid = false
    }
    return nil
}

Если вы используете этот тип вместо sql.NullInt64, он должен быть закодирован, как вы ожидаете.

Вы можете проверить этот пример здесь: http://play.golang.org/p/zFESxLcd-c

Ответ 2

Если вы используете пакет null.v3, вам не потребуется реализовывать какие-либо методы маршала или немаршаля. Это надмножество sql.Null структур и, вероятно, то, что вы хотите.

package main

import "gopkg.in/guregu/null.v3"

type Person struct {
    Name     string      'json:"id"'
    Age      int         'json:"age"'
    NickName null.String 'json:"nickname"' // Optional
}

Если вы хотите увидеть полный веб-сервер Golang, который использует sqlite, nulls и json, вы можете ознакомиться с этим методом.