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

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

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方法。代码如下:

  1. type JsonNullInt64 struct {
  2. sql.NullInt64
  3. }
  4. func (v JsonNullInt64) MarshalJSON() ([]byte, error) {
  5. if v.Valid {
  6. return json.Marshal(v.Int64)
  7. } else {
  8. return json.Marshal(nil)
  9. }
  10. }
  11. func (v *JsonNullInt64) UnmarshalJSON(data []byte) error {
  12. // 将解组到指针可以让我们检测到null
  13. var x *int64
  14. if err := json.Unmarshal(data, &x); err != nil {
  15. return err
  16. }
  17. if x != nil {
  18. v.Valid = true
  19. v.Int64 = *x
  20. } else {
  21. v.Valid = false
  22. }
  23. return nil
  24. }

如果你在代码中使用这个类型来替代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:

  1. type JsonNullInt64 struct {
  2. sql.NullInt64
  3. }
  4. func (v JsonNullInt64) MarshalJSON() ([]byte, error) {
  5. if v.Valid {
  6. return json.Marshal(v.Int64)
  7. } else {
  8. return json.Marshal(nil)
  9. }
  10. }
  11. func (v *JsonNullInt64) UnmarshalJSON(data []byte) error {
  12. // Unmarshalling into a pointer will let us detect null
  13. var x *int64
  14. if err := json.Unmarshal(data, &x); err != nil {
  15. return err
  16. }
  17. if x != nil {
  18. v.Valid = true
  19. v.Int64 = *x
  20. } else {
  21. v.Valid = false
  22. }
  23. return nil
  24. }

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 结构的超集,可能是您想要的。

  1. package main
  2. import "gopkg.in/guregu/null.v3"
  3. type Person struct {
  4. Name string `json:"id"`
  5. Age int `json:"age"`
  6. NickName null.String `json:"nickname"` // 可选
  7. }

如果您想查看一个完整的使用 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.

  1. package main
  2. import "gopkg.in/guregu/null.v3"
  3. type Person struct {
  4. Name string `json:"id"`
  5. Age int `json:"age"`
  6. NickName null.String `json:"nickname"` // Optional
  7. }

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:

确定