根据输出可以判断,go 的 json 包使用的是 RFC3339 标准中定义的格式。接下来测试一下 json.Unmarshal 方法所支持的日期时间格式。
dateStr := "2018-10-12"
var person Person
jsonStr := fmt.Sprintf("{"name":"Wang Wu", "Birth": "%s"}", dateStr)
json.Unmarshal([]byte(jsonStr), &person)
fmt.Println(person.Birth) // 0001-01-01 00:00:00 +0000 UTC
对于形如 2018-10-12 的字符串,json 包并没有成功将其解析,接下来我们把 time 包中支持的所有格式都试一下。

经过试验,发现 json.Unmarshal 方法只支持 RFC3339 和 RFC3339Nano 两种格式的转换。还有一个需要注意的地方,使用 time.Now() 生成的时间是带有一个 Monotonic Time 的,经过 json.Marshal 转换时候,由于 RFC3339 规范里没有存放 Monotonic Time 的位置,会丢掉这一部分。
对于字段为空的处理
json 包对于空值的处理是一个非常容易出错的地方,看下面代码。
type Person struct {
Name string
Age int64
Birth time.Time
Children []Person
}
func main() {
person := Person{}
jsonBytes, _ := json.Marshal(person)
fmt.Println(string(jsonBytes)) // {"Name":"","Age":0,"Birth":"0001-01-01T00:00:00Z","Children":null}
}
当 struct 中的字段没有值时,使用 json.Marshal 方法并不会自动忽略这些字段,而是根据字段的类型输出了他们的默认空值,这往往和我们的预期不一致,json 包提供了对字段的控制手段,我们可以为字段增加 omitempty tag,这个 tag 会在字段值为零值(int 和 float 类型零值是 0,string 类型零值是 "",对象类型零值是 nil)时,忽略该字段。
type PersonAllowEmpty struct {
Name string `json:",omitempty"`
Age int64 `json:",omitempty"`
Birth time.Time `json:",omitempty"`
Children []PersonAllowEmpty `json:",omitempty"`
}
func main() {
person := PersonAllowEmpty{}
jsonBytes, _ := json.Marshal(person)
fmt.Println(string(jsonBytes)) // {"Birth":"0001-01-01T00:00:00Z"}
}
可以看到,这次输出的 json 中只有 Birth 字段了,string、int、对象类型的字段,都因为没有赋值,默认是零值,所以被忽略,对于日期时间类型,由于不可以设置为零值,也就是 0000-00-00 00:00:00,不会被忽略。
需要注意这样的情况:如果一个人的年龄是 0 (对于刚出生的婴儿,这个值是合理的),刚好是 int 字段的零值,在添加 omitempty tag 的情况下,年龄字段会被忽略。
如果想要某一个字段在任何情况下都被 json 包忽略,需要使用如下的写法。
type Person struct {
Name string `json:"-"`
Age int64 `json:"-"`
Birth time.Time `json:"-"`
Children []string `json:"-"`
}
func main() {
birth, _ := time.Parse(time.RFC3339, "1988-12-02T15:04:27+08:00")
person := Person{
Name: "Wang Wu",
Age: 30,
Birth: birth,
Children: []string{},
}
jsonBytes, _ := json.Marshal(person)
fmt.Println(string(jsonBytes)) // {}
}










