翻译自 Custom JSON Marshalling in Go。
Go的 encoding/json
序列化strcut
到JSON数据:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| package main import ( "encoding/json" "os" "time" ) type MyUser struct { ID int64 `json:"id"` Name string `json:"name"` LastSeen time.Time `json:"lastSeen"` } func main() { _ = json.NewEncoder(os.Stdout).Encode( &MyUser{1, "Ken", time.Now()}, ) }
|
序列化的结果:
1
| {"id":1,"name":"Ken","lastSeen":"2009-11-10T23:00:00Z"}
|
但是如果我们想改变一个字段的显示结果我们要怎么做呢?例如,我们想把LastSeen
显示为unix时间戳。
最简单的方式是引入另外一个辅助struct,在MarshalJSON
中使用它进行正确的格式化:
1 2 3 4 5 6 7 8 9 10 11
| func (u *MyUser) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { ID int64 `json:"id"` Name string `json:"name"` LastSeen int64 `json:"lastSeen"` }{ ID: u.ID, Name: u.Name, LastSeen: u.LastSeen.Unix(), }) }
|
这样做当然没有问题,但是如果有很多字段的话就会很麻烦,如果我们能把原始struct嵌入到新的struct中,并让它继承所有不需要改变的字段就太好了:
1 2 3 4 5 6 7 8 9
| func (u *MyUser) MarshalJSON() ([]byte, error) { return json.Marshal(&struct { LastSeen int64 `json:"lastSeen"` *MyUser }{ LastSeen: u.LastSeen.Unix(), MyUser: u, }) }
|
但是等等,问题是这个辅助struct也会继承原始struct的MarshalJSON
方法,这会导致这个方法进入无限循环中,最后堆栈溢出。
解决办法就是为原始类型起一个别名,别名会有原始struct所有的字段,但是不会继承它的方法:
1 2 3 4 5 6 7 8 9 10
| func (u *MyUser) MarshalJSON() ([]byte, error) { type Alias MyUser return json.Marshal(&struct { LastSeen int64 `json:"lastSeen"` *Alias }{ LastSeen: u.LastSeen.Unix(), Alias: (*Alias)(u), }) }
|
同样的技术也可以应用于UnmarshalJSON
方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| func (u *MyUser) UnmarshalJSON(data []byte) error { type Alias MyUser aux := &struct { LastSeen int64 `json:"lastSeen"` *Alias }{ Alias: (*Alias)(u), } if err := json.Unmarshal(data, &aux); err != nil { return err } u.LastSeen = time.Unix(aux.LastSeen, 0) return nil }
|