Golang 中使用 JSON 的小技巧

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

目錄 [−]

  1. 臨時忽略struct空欄位
  2. 臨時添加額外的欄位
  3. 臨時粘合兩個struct
  4. 一個json切分成兩個struct
  5. 臨時改名struct的欄位
  6. 用字串傳遞數字
  7. 容忍字串和數字互轉
  8. 容忍空數組作為對象
  9. 使用 MarshalJSON支援time.Time
  10. 使用 RegisterTypeEncoder支援time.Time
  11. 使用 MarshalText支援非字串作為key的map
  12. 使用 json.RawMessage
  13. 使用 json.Number
  14. 統一更改欄位的命名風格
  15. 使用私人的欄位
  16. 忽略掉一些欄位
  17. 忽略掉一些欄位2

taowen是json-iterator的作者。 序列化和還原序列化需要處理JSON和struct的關係,其中會用到一些技巧。 原文 Golang 中使用 JSON 的小技巧是他的經驗之談,介紹了一些struct解析成json的技巧,以及 json-iterator 庫的一些便利的處理。

有的時候上遊傳過來的欄位是string類型的,但是我們卻想用變成數字來使用。 本來用一個json:",string" 就可以支援了,如果不知道golang的這些小技巧,就要大費周章了。

參考文章:http://attilaolah.eu/2014/09/10/json-and-struct-composition-in-go/

臨時忽略struct空欄位

12345
type User struct {    Email    string `json:"email"`    Password string `json:"password"`    // many more fields…}

如果想臨時忽略掉空Password欄位,可以用omitempty:

123456
json.Marshal(struct {    *User    Password bool `json:"password,omitempty"`}{    User: user,})

臨時添加額外的欄位

12345
type User struct {    Email    string `json:"email"`    Password string `json:"password"`    // many more fields…}

臨時忽略掉空Password欄位,並且添加token欄位

12345678
json.Marshal(struct {    *User    Token    string `json:"token"`    Password bool `json:"password,omitempty"`}{    User: user,    Token: token,})

臨時粘合兩個struct

通過嵌入struct的方式:

1234567891011121314
type BlogPost struct {    URL   string `json:"url"`    Title string `json:"title"`}type Analytics struct {    Visitors  int `json:"visitors"`    PageViews int `json:"page_views"`}json.Marshal(struct{    *BlogPost    *Analytics}{post, analytics})

一個json切分成兩個struct

123456789
json.Unmarshal([]byte(`{  "url": "attila@attilaolah.eu",  "title": "Attila's Blog",  "visitors": 6,  "page_views": 14}`), &struct {  *BlogPost  *Analytics}{&post, &analytics})

臨時改名struct的欄位

12345678910111213141516171819202122232425
type CacheItem struct {    Key    string `json:"key"`    MaxAge int    `json:"cacheAge"`    Value  Value  `json:"cacheValue"`}json.Marshal(struct{    *CacheItem    // Omit bad keys    OmitMaxAge omit `json:"cacheAge,omitempty"`    OmitValue  omit `json:"cacheValue,omitempty"`    // Add nice keys    MaxAge int    `json:"max_age"`    Value  *Value `json:"value"`}{    CacheItem: item,    // Set the int by value:    MaxAge: item.MaxAge,    // Set the nested struct by reference, avoid making a copy:    Value: &item.Value,})

用字串傳遞數字

123
type TestObject struct {    Field1 int    `json:",string"`}

這個對應的json是 {"Field1": "100"}

如果json是 {"Field1": 100} 則會報錯

容忍字串和數字互轉

如果你使用的是jsoniter,可以啟動模糊模式來支援 PHP 傳遞過來的 JSON。

123
import "github.com/json-iterator/go/extra"extra.RegisterFuzzyDecoders()

這樣就可以處理字串和數字類型不對的問題了。比如

12
var val stringjsoniter.UnmarshalFromString(`100`, &val)

又比如

12
var val float32jsoniter.UnmarshalFromString(`"1.23"`, &val)

容忍空數組作為對象

PHP另外一個令人崩潰的地方是,如果 PHP array是空的時候,序列化出來是[]。但是不為空白的時候,序列化出來的是{"key":"value"}。 我們需要把 [] 當成 {} 處理。

如果你使用的是jsoniter,可以啟動模糊模式來支援 PHP 傳遞過來的 JSON。

123
import "github.com/json-iterator/go/extra"extra.RegisterFuzzyDecoders()

這樣就可以支援了

12
var val map[string]interface{}jsoniter.UnmarshalFromString(`[]`, &val)

使用 MarshalJSON支援time.Time

golang 預設會把 time.Time 用字串方式序列化。如果我們想用其他方式表示 time.Time,需要自訂類型並定義 MarshalJSON

123456
type timeImplementedMarshaler time.Timefunc (obj timeImplementedMarshaler) MarshalJSON() ([]byte, error) {    seconds := time.Time(obj).Unix()    return []byte(strconv.FormatInt(seconds, 10)), nil}

序列化的時候會調用 MarshalJSON

123456789
type TestObject struct {    Field timeImplementedMarshaler}should := require.New(t)val := timeImplementedMarshaler(time.Unix(123, 0))obj := TestObject{val}bytes, err := jsoniter.Marshal(obj)should.Nil(err)should.Equal(`{"Field":123}`, string(bytes))

使用 RegisterTypeEncoder支援time.Time

jsoniter 能夠對不是你定義的type自訂JSON編解碼方式。比如對於 time.Time 可以用 epoch int64 來序列化

12345
import "github.com/json-iterator/go/extra"extra.RegisterTimeAsInt64Codec(time.Microsecond)output, err := jsoniter.Marshal(time.Unix(1, 1002))should.Equal("1000001", string(output))

如果要自訂的話,參見 RegisterTimeAsInt64Codec 的實現代碼

使用 MarshalText支援非字串作為key的map

雖然 JSON 標準裡只支援 string 作為 keymap。但是 golang 通過 MarshalText() 介面,使得其他類型也可以作為 mapkey。例如

1234
f, _, _ := big.ParseFloat("1", 10, 64, big.ToZero)val := map[*big.Float]string{f: "2"}str, err := MarshalToString(val)should.Equal(`{"1":"2"}`, str)

其中 big.Float 就實現了 MarshalText()

使用 json.RawMessage

如果部分json文檔沒有標準格式,我們可以把原始的資訊用[]byte儲存下來。

1234567
type TestObject struct {    Field1 string    Field2 json.RawMessage}var data TestObjectjson.Unmarshal([]byte(`{"field1": "hello", "field2": [1,2,3]}`), &data)should.Equal(` [1,2,3]`, string(data.Field2))

使用 json.Number

預設情況下,如果是 interface{} 對應數位情況會是 float64 類型的。如果輸入的數字比較大,這個表示會有損精度。所以可以 UseNumber() 啟用 json.Number 來用字串表示數字。

12345
decoder1 := json.NewDecoder(bytes.NewBufferString(`123`))decoder1.UseNumber()var obj1 interface{}decoder1.Decode(&obj1)should.Equal(json.Number("123"), obj1)

jsoniter 支援標準庫的這個用法。同時,擴充了行為使得 Unmarshal 也可以支援 UseNumber 了。

1234
json := Config{UseNumber:true}.Froze()var obj interface{}json.UnmarshalFromString("123", &obj)should.Equal(json.Number("123"), obj)

統一更改欄位的命名風格

經常 JSON 裡的欄位名 Go 裡的欄位名是不一樣的。我們可以用 field tag 來修改。

12345678
output, err := jsoniter.Marshal(struct {    UserName      string `json:"user_name"`    FirstLanguage string `json:"first_language"`}{    UserName:      "taowen",    FirstLanguage: "Chinese",})should.Equal(`{"user_name":"taowen","first_language":"Chinese"}`, string(output))

但是一個個欄位來設定,太麻煩了。如果使用 jsoniter,我們可以統一設定命名風格。

123456789101112
import "github.com/json-iterator/go/extra"extra.SetNamingStrategy(LowerCaseWithUnderscores)output, err := jsoniter.Marshal(struct {    UserName      string    FirstLanguage string}{    UserName:      "taowen",    FirstLanguage: "Chinese",})should.Nil(err)should.Equal(`{"user_name":"taowen","first_language":"Chinese"}`, string(output))

使用私人的欄位

Go 的標準庫只支援 public 的 field。jsoniter 額外支援了 private 的 field。需要使用 SupportPrivateFields() 來開啟開關。

123456789
import "github.com/json-iterator/go/extra"extra.SupportPrivateFields()type TestObject struct {    field1 string}obj := TestObject{}jsoniter.UnmarshalFromString(`{"field1":"Hello"}`, &obj)should.Equal("Hello", obj.field1)

下面是我補充的內容

忽略掉一些欄位

原文中第一節有個錯誤,我更正過來了。omitempty不會忽略某個欄位,而是忽略空的欄位,當欄位的值為空白值的時候,它不會出現在JSON資料中。

如果想忽略某個欄位,需要使用 json:"-"格式。

12345
type User struct {    Email    string `json:"email"`    Password string `json:"password"`    // many more fields…}

如果想臨時忽略掉空Password欄位,可以用-:

123456
json.Marshal(struct {    *User    Password bool `json:"-"`}{    User: user,})

忽略掉一些欄位2

如果不想更改原struct,還可以使用下面的方法:

12345678910111213141516
type User struct {    Email    string `json:"email"`    Password string `json:"password"`    // many more fields…}type omit *struct{}type PublicUser struct {    *User    Password omit `json:"-"`}json.Marshal(PublicUser{    User: user,})
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.