<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>

          第 74 期周刊題目答案解析

          共 3235字,需瀏覽 7分鐘

           ·

          2020-12-22 13:56

          大家好,我是站長(zhǎng) polarisxu。

          《Go 語(yǔ)言愛好者周刊第 74 期》有一道題,以下代碼輸出什么?

          package?main

          import?(
          ?"encoding/json"
          ?"fmt"
          ?"time"
          )

          func?main()?{
          ?t?:=?struct?{
          ??time.Time
          ??N?int
          ?}{
          ??time.Date(2020,?12,?20,?0,?0,?0,?0,?time.UTC),
          ??5,
          ?}

          ?m,?_?:=?json.Marshal(t)
          ?fmt.Printf("%s",?m)
          }

          A:{"Time": "2020-12-20T00:00:00Z", "N": 5};B:"2020-12-20T00:00:00Z";C:{"N": 5};D:

          答題結(jié)果如下:

          雖然參與投票的人不是很多,但從結(jié)果還是能反饋出來一些問題的,不少人知曉了答案是 B,但還是想不明白為什么。所以寫篇文章對(duì)該題進(jìn)行解析。大家不用在乎是不是刁鉆的題目,通過題目能學(xué)到知識(shí)才是最重要的。

          01

          對(duì)一個(gè)結(jié)構(gòu)體實(shí)例進(jìn)行 json 系列化,直覺很容易選 A。但很顯然,不可能這么簡(jiǎn)單,因此需要仔細(xì)看代碼。至于選 C 和 D 的人,多半是瞎猜的吧,當(dāng)然選 B 也不排除有瞎猜的。

          為什么選 B 呢?我想借助一個(gè)例子講解。

          package?main

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

          type?Person?struct?{
          ??name??string
          ??hobby?string
          }?

          func?main()?{
          ??person?:=?Person{name:?"polarisxu",?hobby:?"Golang"}
          ??m,?_?:=?json.Marshal(person)
          ??fmt.Printf("%s",?m)
          }

          如果你認(rèn)為輸出 {"name":"polarisxu","hobby":"Golang"},那你得去補(bǔ)補(bǔ) encoding/json 包的知識(shí)了。要想輸出 ?{"name":"polarisxu","hobby":"Golang”},一般我們會(huì)這么做:將 Person 的字段導(dǎo)出,同時(shí)設(shè)置上 tag。

          type?Person?struct?{
          ??Name??string?`json:"name"`
          ??Hobby?string?`json:"hobby"`
          }

          但如果我們不想導(dǎo)出 Person 的字段呢?可以通過實(shí)現(xiàn) Marshaler 來做到。

          func?(p?Person)?MarshalJSON()?([]byte,?error)?{
          ?return?[]byte(`{"name":"`+p.name+`","hobby":"`+p.hobby+`"}`),?nil
          }

          02

          回到題目上,time.Time 是什么類型?

          type?Time?struct?{
          ????//?contains?filtered?or?unexported?fields
          }

          這是一個(gè)沒有導(dǎo)出任何字段的結(jié)構(gòu)體類型,因此它肯定實(shí)現(xiàn)了 Marshaler 接口。

          //?MarshalJSON?implements?the?json.Marshaler?interface.
          //?The?time?is?a?quoted?string?in?RFC?3339?format,?with?sub-second?precision?added?if?present.
          func?(t?Time)?MarshalJSON()?([]byte,?error)?{
          ?if?y?:=?t.Year();?y?0?||?y?>=?10000?{
          ??//?RFC?3339?is?clear?that?years?are?4?digits?exactly.
          ??//?See?golang.org/issue/4556#c15?for?more?discussion.
          ??return?nil,?errors.New("Time.MarshalJSON:?year?outside?of?range?[0,9999]")
          ?}

          ?b?:=?make([]byte,?0,?len(RFC3339Nano)+2)
          ?b?=?append(b,?'"')
          ?b?=?t.AppendFormat(b,?RFC3339Nano)
          ?b?=?append(b,?'"')
          ?return?b,?nil
          }

          這么說,答案不應(yīng)該就是 A 嗎?別急。

          03

          如果 t 是這么定義的:

          t?:=?struct?{
          ??Time?time.Time
          ??N?int
          }{
          ??time.Date(2020,?12,?20,?0,?0,?0,?0,?time.UTC),
          ??5,
          }

          那結(jié)果就是 A。而題目中,time.Time 是內(nèi)嵌的。你學(xué)習(xí) Go 時(shí),應(yīng)該看過通過內(nèi)嵌來模擬繼承的功能吧!

          正是因?yàn)閮?nèi)嵌,t 的方法集包括了 time.Time 的方法集,所以,t 自動(dòng)實(shí)現(xiàn)了 Marshaler 接口。因此答案是 B。

          其實(shí)這道題的情況,在日常工作中還真有可能遇到。所以,當(dāng)你內(nèi)嵌某個(gè)類型時(shí),特別這個(gè)類型不是你自己定義的,需要留意這種情況。

          一般解決這個(gè)問題的方法有兩種:1)不內(nèi)嵌;2)重新實(shí)現(xiàn) MarshalJSON 方法。

          然而這道題無(wú)法重新實(shí)現(xiàn) MarshalJSON 方法,因?yàn)榻Y(jié)構(gòu)體類型是匿名的。只能通過不內(nèi)嵌來得到正確的結(jié)果。

          04

          最后一起看下 json.Marshal 函數(shù)的文檔,主要看下面這段:

          Marshal traverses the value v recursively. If an encountered value implements the Marshaler interface and is not a nil pointer, Marshal calls its MarshalJSON method to produce JSON. If no MarshalJSON method is present but the value implements encoding.TextMarshaler instead, Marshal calls its MarshalText method and encodes the result as a JSON string. The nil pointer exception is not strictly necessary but mimics a similar, necessary exception in the behavior of UnmarshalJSON.

          大意是說,如果值實(shí)現(xiàn)了 json.Marshaler 接口并且不是 nil 指針,則 Marshal 函數(shù)會(huì)調(diào)用其 MarshalJSON 方法以生成 JSON。如果不存在 MarshalJSON 方法,但該值實(shí)現(xiàn) encoding.TextMarshaler 接口,則 Marshal 函數(shù)調(diào)用其 MarshalText 方法并將結(jié)果編碼為 JSON 字符串。

          可見,json.Marshal 函數(shù)優(yōu)先調(diào)用 MarshalJSON,然后是 MarshalText,如果都沒有,才會(huì)走正常的類型編碼邏輯。



          往期推薦

          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù)?ebook?獲取;還可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

          瀏覽 32
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(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>
                  中文人妻无码一区二区三区不卡 | 61无码| 91色情网老熟女 | 激情五月俺也去 | 一级日韩|