背景:time.NOW()获得的时间格式是RFC3339格式的,需要把他按照yyyy/MM/dd hh:mm:ss的格式进行json序列化并返回给前端
解决方法:
-
给目标类型创建一个别名,并对该别名重载MarshalJSON方法,例如:
//给time.Time创建别名为jsonTime type jsonTime time.Time //包含time.Time类型字段的结构体,改用jsonTime这个类型 type Message struct { CreateAt jsonTime `json:"createAt"` } //重写jsonTime类型的MarchalJSON方法,以改变rfc3339的时间格式 func (this jsonTime) MarshalJSON() ([]byte, error) { var stamp = fmt.Sprintf("\"%s\"", time.Time(this).Format("2006-01-02 15:04:05")) return []byte(stamp), nil }
如此直接对Message对象进行序列化,其中CreateAt字段的格式就会变成自定义格式。
var msg = Message{ CreateAt: jsonTime(time.Now()), } marshal, _ := json.Marshal(msg) fmt.Println(string(marshal)) //输出结果: {"createAt":"2021-02-02 21:29:36"}
-
直接对目标结构体重载MarshalJSON方法,例如:
func (d Message) MarshalJSON() ([]byte, error) { type Alias Message return json.Marshal(struct { Alias CreateAt string `json:"createAt"` }{ Alias: Alias(d), CreateAt: d.CreateAt.Format("2006/01/02 15:04:05"), }) }
这里利用了匿名结构体和json序列化时的同名字段覆盖特点。
- 注意到,这里是给Message取了一个别名Alias,这么做的目的是避免对Message.MarchalJSON进行递归调用。
- 之后构造了一个内容为Alias和CreateAt两个字段的匿名结构体,其中CreateAt的标签与Message中的createAt标签一样为
json:"createAt"
. - 在return json.Marshal时,会优先取
CreateAt: d.CreateAt.Format("2006/01/02 15:04:05"),
的结果作为json串中createAt属性的值,至于是如何实现的,阅读源码之后发现涉及到反射机制,往里钻了几层还是没搞懂,有机会再探究吧。
在执行完json.Marshal(s)后,得到的marshal如图:
至于s.Alias.CreateAt和s.CreateAt序列化结果究竟如何被替换,没搞懂。
这么做似乎有点麻烦,但我感觉非常巧妙,类似地,还可以实现一个结构体对象序列化时添加一些额外的字段,例如:
type Book struct {
Title string
Author string
}
type FakeBook Book
func (b Book) MarshalJSON() ([]byte,error) {
return json.Marshal(struct {
FakeBook
Genre string
}{
FakeBook: FakeBook(b),Genre: "Satire",})
}
//对Book对象的序列化结果:
{
"Title": "Catch-22","Author": "Joseph Heller","Genre": "Satire" //可以发现确实多了一个Genre字段
}