本文仅为翻译,原文链接->Custom JSON Marshalling in Go
Go自带一个包encoding/json
,这个包使序列化结构体(struct
)和反序列化json
变得异常简单。
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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"}
|
但是,如果我想改变序列化之后其中一个field的值应该怎么做呢?比如,我们想要把LastSeen修改为Unix时间戳的形式。
一个简单的方法就是使用一个辅助的结构体并在调用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(), }) }
|
这种方法当时使可以的,但是如果一个结构体有很多个字段field
,那么这种方法就比较麻烦了(笨重.jpg)。最好可以直接把原来的结构体放进辅助结构体里,让辅助结构体继承所有不要被改变的field,只添加需要被改变的field。
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, }) }
|
但是问题是,这个辅助的结构体同样会继承MarshalJSON
这个方法,这样就会导致无限循环(其实就是MarshalJSON
调用MarshalJSON
)。
解决方法就是给原始的结构体做一个别名,这个别名会继承原始结构体的所有field但不会继承方法。
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 }
|