sql.NullInt64 в JSON - PullRequest
       17

sql.NullInt64 в JSON

0 голосов
/ 31 марта 2019

У меня есть схема базы данных, где возраст необязательно . Но когда в Go через мой интерфейс RestFUL я выводил данные в виде JSON, я получал очень уродливый ответ, например {"ID":1,"Name":"John","age":{"Int64":0,"Valid":false}} для возраста. Я ожидал, что нулевое значение будет опущено. Чего мне не хватает?

// CREATE TABLE users (
//        id INT AUTO_INCREMENT PRIMARY KEY,
//        name VARCHAR(50) NOT NULL,
//        age INT
// );

// Age is optional

type user struct {
    ID   int           `db:"id"`
    Name string        `db:"name"`
    Age  sql.NullInt64 `db:"age" json:"age,omitempty"`
}

func main() {
    u := user{ID: 1, Name: "John"}
    j, _ := json.Marshal(u)
    fmt.Printf("%s", j)
}

// Expected: {"ID":1,"Name":"John"} since age is NULL/empty

https://play.golang.org/p/PS1-4Gw9h5u

Ответы [ 2 ]

2 голосов
/ 31 марта 2019

sql.NullInt64 существует, потому что SQL null не может быть представлен как Go int.Это третье состояние, которое не может быть представлено каким-либо значением int.

Одним из решений этого было бы представление таких значений SQL как *int, но это потребовало бы выделения для случаев, когда значение не являетсяnull в базе данных, а выделения плохо влияют на производительность.

Разработчики пакета SQL придумали решение NullInt64, которое кодирует третье состояние нуля как дополнительный логический Valid.Это не очень хорошее решение, но это лучшее, что мы можем получить.

Я не уверен, возможно ли написать маршаллер JSON для NullInt64, который бы работал так, как вы ожидаете.

Все еще естьпроблема "третьего состояния" при сортировке в JSON.С ,omitempty a 0 int также будет опущено, так как вы можете отличить 0 от «не существует» / null?

В любом случае они не написали собственный маршаллер для NullInt64, поэтому он просто кодируетв качестве структуры.

Вы можете создать тип псевдонима для NullInt64, написать маршаллер JSON для кодирования того, как вы хотите JSON (вам нужен псевдоним, потому что вы не можете добавлять методы в типыиз других пакетов).Вам также нужно будет разыграть между вашими NullInt64 и sql.NullInt64.

0 голосов
/ 31 марта 2019

Вы можете использовать пользовательский тип для null int64. Этот тип работает, как вы ожидали.

Пример использования:

package main

import (
    "encoding/json"
    "database/sql/driver" 
    "fmt"
    "strconv"
)

type user struct {
    ID   int             `db:"id"`
    Name string          `db:"name"`
    // pointer needed to omitempty work, otherwise output will be "age": null
    Age  *NullInt64 `db:"age" json:"age,omitempty"`
}

func main() {
    u := user{ID: 1, Name: "John"}
    j, _ := json.Marshal(u)
    fmt.Printf("%s\n", j)
}

//    Output:
//        {"ID":1,"Name":"John"}

type NullInt64 struct {
    Val     int64
    IsValid bool
}

func NewNullInt64(val interface{}) NullInt64 {
    ni := NullInt64{}
    ni.Set(val)
    return ni
}

func (ni *NullInt64) Scan(value interface{}) error {
    ni.Val, ni.IsValid = value.(int64)
    return nil
}

func (ni NullInt64) Value() (driver.Value, error) {
    if !ni.IsValid {
        return nil, nil
    }
    return ni.Val, nil
}

func (ni *NullInt64) Set(val interface{}) {
    ni.Val, ni.IsValid = val.(int64)
}

func (ni NullInt64) MarshalJSON() ([]byte, error) {
    if !ni.IsValid {
        return []byte(`null`), nil
    }

    return []byte(strconv.FormatInt(ni.Val, 10)), nil
}

func (ni *NullInt64) UnmarshalJSON(data []byte) error {
    if data == nil || string(data) == `null` {
        ni.IsValid = false
        return nil
    }

    val, err := strconv.ParseInt(string(data), 10, 64)
    if err != nil {
        ni.IsValid = false
        return err
    }

    ni.Val = val
    ni.IsValid = true

    return nil
}

func (ni NullInt64) String() string {
    if !ni.IsValid {
        return `<nil>`
    }

    return strconv.FormatInt(ni.Val, 10)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...