如何以良好的方式处理 SQL 中的 NULL 值和 JSON?

huangapple go评论87阅读模式
英文:

How can I work with SQL NULL values and JSON in a good way?

问题

Go类型如Int64String不能存储空值,所以我发现我可以使用sql.NullInt64sql.NullString来处理这个问题。

但是当我在结构体中使用这些类型,并使用json包从结构体生成JSON时,生成的JSON格式与使用常规的Int64String类型时不同。

这是因为sql.Null***也是一个结构体,所以JSON多了一层结构。

有没有好的解决方法,或者我应该在我的SQL数据库中不使用NULL值?

英文:

Go types like Int64 and String cannot store null values,
so I found I could use sql.NullInt64 and sql.NullString for this.

But when I use these in a Struct,
and generate JSON from the Struct with the json package,
then the format is different to when I use regular Int64 and String types.

The JSON has an additional level because the sql.Null*** is also a Struct.

Is there a good workaround for this,
or should I not use NULLs in my SQL database?

答案1

得分: 79

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 {
    // 将解组到指针可以让我们检测到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

英文:

Types like sql.NullInt64 do not implement any special handling for JSON marshaling or unmarshaling, so the default rules apply. Since the type is a struct, it gets marshalled as an object with its fields as attributes.

One way to work around this is to create your own type that implements the json.Marshaller / json.Unmarshaler interfaces. By embedding the sql.NullInt64 type, we get the SQL methods for free. Something like this:

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
}

If you use this type in place of sql.NullInt64, it should be encoded as you expect.

You can test this example here: http://play.golang.org/p/zFESxLcd-c

答案2

得分: 44

如果您使用 null.v3 包,您将不需要实现任何 marshal 或 unmarshal 方法。它是 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"` // 可选
}

如果您想查看一个完整的使用 sqlite、nulls 和 json 的 Golang web 服务器,您可以参考 这个 gist

英文:

If you use the null.v3 package, you won't need to implement any of the marshal or unmarshal methods. It's a superset of the sql.Null structs and is probably what you want.

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
}

If you'd like to see a full Golang webserver that uses sqlite, nulls, and json you can consult this gist.

huangapple
  • 本文由 发表于 2015年10月12日 09:59:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/33072172.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定