众所周知,在 MySQL 中,有一些字段不可逃避的要有 null
值。比如 datetime
字段,如果没有值,那么只能是 null
值。如果在 Golang 中不去做处理,而直接使用 time.Time
类型,则会报错,提示不能将 sql.NullTime
值转换为 time.Time
。
这时候,一脸懵逼。
当你开心的将 struct 中 time.Time
类型改为 sql.NullTime
类型的时候,以为自己解决了这个问题。
但是之后将从 DB 取出来的数据 Marshal
到 一个 time.Time
类型的 struct 的时候,出现了惊人的一幕。
Marshal
的结果,他不是一个 time.Time
,而是一个 sql.NullTime
的结构。
{"Time":"2020-07-19T20:10:01.972722+08:00","Valid":true}
那么如何让 sql.NullTime
的 Marshal
和 Unmarshal
与 time.Time
表现一致呢?
{"Time":"2020-07-19T20:10:01.972722+08:00","Valid":true}
=>
"2020-07-19T20:10:01.972722+08:00"
可以使用 json.Marshaler
的 interface 下 MarshalJSON
和 UnmarshalJSON
方法:
如下大法可以轻松破解此问题。
(参考:https://zhuanlan.zhihu.com/p/21933959 )
package types
import (
"database/sql"
"encoding/json"
"time"
)
type NullTime struct {
sql.NullTime
}
func (v NullTime) MarshalJSON() ([]byte, error) {
if v.Valid {
return json.Marshal(v.Time)
} else {
return json.Marshal(nil)
}
}
func (v *NullTime) UnmarshalJSON(data []byte) error {
var s *time.Time
if err := json.Unmarshal(data, &s); err != nil {
return err
}
if s != nil {
v.Valid = true
v.Time = *s
} else {
v.Valid = false
}
return nil
}
注意 MarshalJSON
方法是「非指针」的
利用 Marshaler
的 MarshalJSON
和 UnmarshalJSON
的方法来处理 null
值的情况。具体依据来源于 golang json marshal 源码 https://github.com/golang/go/blob/master/src/encoding/json/encode.go#L494