<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Go 自定義 Json 序列化規(guī)則

          共 3397字,需瀏覽 7分鐘

           ·

          2022-04-28 23:17

          點(diǎn)擊上方“Go語言進(jìn)階學(xué)習(xí)”,進(jìn)行關(guān)注

          回復(fù)“Go語言”即可獲贈從入門到進(jìn)階共10本電子書

          浮云游子意,落日故人情。

          開發(fā)過程中,我們經(jīng)常會使用 JSON 作為數(shù)據(jù)傳輸格式。而這離不開對 JSON 數(shù)據(jù)的編解碼工作,在 Go 中,encoding/json 包提供了這些能力。

          我們可以使用 encoding/json 包的 Encoder.Encode() 和 Marshal() 實(shí)現(xiàn) Json 序列化,使用 Decoder.Decode() 和 Unmarshal() 實(shí)現(xiàn) Json 反序列化。

          示例如下

          package?main

          import?(
          ?"encoding/json"
          ?"os"
          )

          type?Metric?struct?{
          ?Name??string?`json:"name"`
          ?Value?int64??`json:"value"`
          }

          func?main()?{
          ?_?=?json.NewEncoder(os.Stdout).Encode(
          ??[]*Metric{
          ???{"vv",?12},
          ???{"tz",?9},
          ???{"ss",?82},
          ??},
          ?)
          }

          輸出

          [{"name":"vv","value":12},{"name":"tz","value":9},{"name":"ss","value":82}]

          提出問題

          以上述結(jié)構(gòu)體 Metric 為例,它代表的是統(tǒng)計(jì)指標(biāo)。其中 Name 是指標(biāo)名,Value 是指標(biāo)值。

          假設(shè)程序接收外部 Json 數(shù)據(jù)時(shí),存在指標(biāo)值為浮點(diǎn)數(shù)的情況,如下所示。

          func?main()?{
          ?var?metric?Metric
          ?err?:=?json.Unmarshal([]byte(`{"name":?"tq",?"value":?13.14}`),?&metric)
          ?if?err?!=?nil?{
          ??panic(err)
          ?}
          ?fmt.Println(metric)
          }

          由于 Metric 結(jié)構(gòu)體中定義的 Value 字段為 int64,此時(shí)對 Json 數(shù)據(jù)的反序列化將得到以下錯(cuò)誤

          panic:?json:?cannot?unmarshal?number?13.14?into?Go?struct?field?Metric.value?of?type?int64

          這種問題應(yīng)該如何處理?我們能否在不改變原有結(jié)構(gòu)體定義的情況下,處理這種情況。

          解決方法

          在 encoding/json 中,有兩個(gè)非常重要的接口。

          //?Marshaler?is?the?interface?implemented?by?types?that
          //?can?marshal?themselves?into?valid?JSON.
          type?Marshaler?interface?{
          ?MarshalJSON()?([]byte,?error)
          }

          //?Unmarshaler?is?the?interface?implemented?by?types
          //?that?can?unmarshal?a?JSON?description?of?themselves.
          //?The?input?can?be?assumed?to?be?a?valid?encoding?of
          //?a?JSON?value.?UnmarshalJSON?must?copy?the?JSON?data
          //?if?it?wishes?to?retain?the?data?after?returning.
          //
          //?By?convention,?to?approximate?the?behavior?of?Unmarshal?itself,
          //?Unmarshalers?implement?UnmarshalJSON([]byte("null"))?as?a?no-op.
          type?Unmarshaler?interface?{
          ?UnmarshalJSON([]byte)?error
          }

          如果類型 T 實(shí)現(xiàn)了 Marshaler 或 Unmarshaler 接口方法,就能自定義了序列化和反序列化規(guī)則。

          有了這個(gè)理解基礎(chǔ),那么對于上文中的問題,我們很自然地想到,讓 Metric 結(jié)構(gòu)體實(shí)現(xiàn)UnmarshalJSON()。

          那么首先想到最簡單的方式,是引入一個(gè)臨時(shí)結(jié)構(gòu)體:讓其 Value 字段定義為 float64,其他字段維持和原結(jié)構(gòu)體一致。

          func?(u?*Metric)?UnmarshalJSON(data?[]byte)?error?{
          ?type?tmp?struct?{
          ??Name??string??`json:"name"`
          ??Value?float64?`json:"value"`
          ?}
          ?t?:=?&tmp{
          ??Name:??u.Name,
          ??Value:?float64(u.Value),
          ?}
          ?err?:=?json.Unmarshal(data,?t)
          ?if?err?!=?nil?{
          ??return?nil
          ?}
          ?//?注意 Unmarshaler 接口定義的以下規(guī)則:
          ?//?UnmarshalJSON?must?copy?the?JSON?data
          ?//?if?it?wishes?to?retain?the?data?after?returning.
          ?u.Name?=?t.Name
          ?u.Value?=?int64(t.Value)
          ?return?nil
          }

          這樣做能夠解決我們的問題,但并不優(yōu)雅。

          我們可以使用結(jié)構(gòu)體的繼承。這樣,當(dāng)結(jié)構(gòu)體存在大量字段時(shí),我們僅定義需要更改的字段即可。

          func?(u?*Metric)?UnmarshalJSON(data?[]byte)?error?{
          ?t?:=?&struct?{
          ??Value?float64?`json:"value"`
          ??*Metric
          ?}{
          ??Value:??float64(u.Value),
          ??Metric:?u,
          ?}
          ?if?err?:=?json.Unmarshal(data,?&t);?err?!=?nil?{
          ??return?err
          ?}
          ?u.Value?=?int64(t.Value)
          ?return?nil
          }

          不過這樣子會引出新的問題:繼承原結(jié)構(gòu)體的新 strcut 同樣會繼承原結(jié)構(gòu)體的方法(UnmarshalJSON() 方法),這將無限循環(huán),最終導(dǎo)致堆棧溢出。

          fatal?error:?stack?overflow

          最佳解決方案是以結(jié)構(gòu)體新類型,讓新類型獲得原始結(jié)構(gòu)體的所有字段屬性,但是卻不會繼承原有結(jié)構(gòu)體的方法。

          func?(u?*Metric)?UnmarshalJSON(data?[]byte)?error?{
          ?type?AliasMetric?Metric
          ?t?:=?&struct?{
          ??Value?float64?`json:"value"`
          ??*AliasMetric
          ?}{
          ??Value:???????float64(u.Value),
          ??AliasMetric:?(*AliasMetric)(u),
          ?}
          ?if?err?:=?json.Unmarshal(data,?&t);?err?!=?nil?{
          ??return?err
          ?}
          ?u.Value?=?int64(t.Value)
          ?return?nil
          }

          這樣,就完美解決了我們的問題。

          總結(jié)

          在 Json 數(shù)據(jù)的處理中,我們可以通過為對象實(shí)現(xiàn) Marshaler 或 Unmarshaler 接口方法,從而制定我們想要的序列化和反序列化規(guī)則。

          本文通過一個(gè)簡單的示例,展示了如何通過實(shí)現(xiàn)結(jié)構(gòu)體的 UnmarshalJSON() 方法,而達(dá)到反序列化時(shí)更改字段屬性的目的。

          同理,我們可以為結(jié)構(gòu)體定義 MarshalJSON() 方法,制定序列化規(guī)則,這就留給讀者自行嘗試了。

          -------------------?End?-------------------

          往期精彩文章推薦:

          歡迎大家點(diǎn)贊,留言,轉(zhuǎn)發(fā),轉(zhuǎn)載,感謝大家的相伴與支持

          想加入Go學(xué)習(xí)群請?jiān)诤笈_回復(fù)【入群

          萬水千山總是情,點(diǎn)個(gè)【在看】行不行

          瀏覽 31
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美日韩性爱一区二区 | 日韩日韩日韩日韩AV | 亚洲视频看看 | 伊人色色综合 | 99免费热播视频 |