翻译自 Custom JSON Marshalling in Go。
Go的 encoding/json序列化strcut到JSON数据:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | package mainimport (
 "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中使用它进行正确的格式化:
| 12
 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中,并让它继承所有不需要改变的字段就太好了:
| 12
 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所有的字段,但是不会继承它的方法:
| 12
 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方法:
| 12
 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
 }
 
 |