工作中使用golang時,遇到了一個問題。聲明的struct含time.Time類型。使用json格式化struct時,time.Time被格式化成”2006-01-02T15:04:05.999999999Z07:00“格式。
代碼如下
package jsontest import ( "encoding/json" "testing" "time" ) type Person struct { Id int64 `json:"id"` Name string `json:"name"` Birthday time.Time `json:"birthday"` } func TestTimeJson(t *testing.T) { now := time.Now() t.Log(now) src := `{"id":5,"name":"xiaoming","birthday":"2016-06-30T16:09:51.692226358+08:00"}` p := new(Person) err := json.Unmarshal([]byte(src), p) if err != nil { t.Fatal(err) } t.Log(p) t.Log(p.Birthday) js, _ := json.Marshal(p) t.Log(string(js)) }
golang的time.Time的默認json格式化格式叫做RFC3339。好像是一種國際標准,被推薦用作json時間的標准格式。但是android端不需要這種,而且不容易解析。
經過google,golang文檔等途徑,寫了一個既能解決問題,又不會對源代碼產生影響的解決方案。
先看一下google的解決方案
type Time struct { time.Time } // returns time.Now() no matter what! func (t *Time) UnmarshalJSON(b []byte) error { // you can now parse b as thoroughly as you want *t = Time{time.Now()} return nil } type Config struct { T Time } func main() { c := Config{} json.Unmarshal([]byte(`{"T": "bad-time"}`), &c) fmt.Printf("%+v\n", c) }
原文:http://stackoverflow.com/questions/25087960/json-unmarshal-time-that-isnt-in-rfc-3339-format
但是這樣寫會對原有的struct產生影響。在映射數據庫時,就不行了。此時,心里一片烏雲。。。
后來在看url包時,發現了系統包的一種聲明數據類型的方式
type Values map[string][]string
根據這種聲明方式,受到了啟發,便寫了一個自己的方法,如下
type Time time.Time const ( timeFormart = "2006-01-02 15:04:05" ) func (t *Time) UnmarshalJSON(data []byte) (err error) { now, err := time.ParseInLocation(`"`+timeFormart+`"`, string(data), time.Local) *t = Time(now) return } func (t Time) MarshalJSON() ([]byte, error) { b := make([]byte, 0, len(timeFormart)+2) b = append(b, '"') b = time.Time(t).AppendFormat(b, timeFormart) b = append(b, '"') return b, nil }
同時,將Person的Birthday的類型改為Time,成功的實現的json格式化與json解析。應用到自己的項目中,不會對原有的數據庫映射產生影響。需要轉換類型的時候,只需Time.(xx)便可,很方便。
以為到這里便結束了。后面還有一個小坑在等我。
struct默認打印結果是將其成員完全打印出來。如把Person打印出來,便是
&{5 xiaoming {63602870991 0 0x6854a0}}
我想要的是時間,{63602870991 0 0x6854a0} 是個什么。后來發現,自己的Time類型相當於繼承了time.TIme的成員。沒有像java一樣繼承方法。調用了struct默認打印方式。golang有沒有類似於java的toString方法呢。
當然有,而且很簡單
func (t Time) String() string { return time.Time(t).Format(timeFormart) }
這樣,就實現了更改打印輸出方式
&{5 xiaoming 2016-06-30 16:09:51}
最后,把全部代碼貼出
package jsontest import ( "encoding/json" "testing" "time" ) type Time time.Time const ( timeFormart = "2006-01-02 15:04:05" ) func (t *Time) UnmarshalJSON(data []byte) (err error) { now, err := time.ParseInLocation(`"`+timeFormart+`"`, string(data), time.Local) *t = Time(now) return } func (t Time) MarshalJSON() ([]byte, error) { b := make([]byte, 0, len(timeFormart)+2) b = append(b, '"') b = time.Time(t).AppendFormat(b, timeFormart) b = append(b, '"') return b, nil } func (t Time) String() string { return time.Time(t).Format(timeFormart) } type Person struct { Id int64 `json:"id"` Name string `json:"name"` Birthday Time `json:"birthday"` } func TestTimeJson(t *testing.T) { now := Time(time.Now()) t.Log(now) src := `{"id":5,"name":"xiaoming","birthday":"2016-06-30 16:09:51"}` p := new(Person) err := json.Unmarshal([]byte(src), p) if err != nil { t.Fatal(err) } t.Log(p) t.Log(time.Time(p.Birthday)) js, _ := json.Marshal(p) t.Log(string(js)) }
由此,可以對任意struct增加 UnmarshalJSON , MarshalJSON , String 方法,實現自定義json輸出格式與打印方式。