<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 的處理

          共 4681字,需瀏覽 10分鐘

           ·

          2022-06-09 05:38

          點擊上方藍色“Go語言中文網(wǎng)”關注,每天一起學 Go

          我的第一個 Go 工程需要處理一堆 JSON 測試固件并把 JSON 數(shù)據(jù)作為參數(shù)傳給我們搭建的 API 處理。另一個團隊為了給 API 提供語言無關的、可預期的輸入和輸出,創(chuàng)建了這些測試固件。

          在強類型語言中,JSON 通常很難處理 —— JSON 類型有字符串、數(shù)字、字典和數(shù)組。如果你使用的語言是 javascript、python、ruby 或 PHP,那么 JSON 有一個很大的好處就是在解析和編碼數(shù)據(jù)時你不需要考慮類型。

          //?in?PHP
          $object?=?json_decode('{"foo":"bar"}');

          //?in?javascript
          const?object?=?JSON.parse('{"foo":"bar"}')

          在強類型語言中,你需要自己去定義怎么處理 JSON 對象的字符串、數(shù)字、字典和數(shù)組。在 Go 語言中,你使用內建的 API 時需要考慮如何更好地把一個 JSON 文件轉換成 Go 的數(shù)據(jù)結構。我不打算深入研究在 Go 中如何處理 JSON 這個復雜的話題,我只列出兩個代碼的例子來闡述下這個問題。源碼詳情請見 Go 實例教程[1]

          解析/序列化為 map[string]interface

          首先,來看這個程序

          package?main

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


          func?main()?{

          ????byt?:=?[]byte(`{
          ????????"num":6.13,
          ????????"strs":["a","b"],
          ????????"obj":{"foo":{"bar":"zip","zap":6}}
          ????}`
          )
          ????var?dat?map[string]interface{}
          ????if?err?:=?json.Unmarshal(byt,?&dat);?err?!=?nil?{
          ????????panic(err)
          ????}
          ????fmt.Println(dat)

          ????num?:=?dat["num"].(float64)
          ????fmt.Println(num)

          ????strs?:=?dat["strs"].([]interface{})
          ????str1?:=?strs[0].(string)
          ????fmt.Println(str1)

          ????obj?:=?dat["obj"].(map[string]interface{})
          ????obj2?:=?obj["foo"].(map[string]interface{})
          ????fmt.Println(obj2)

          }

          我們把 JSON 數(shù)據(jù)從 byt 變量反序列化(如解析、解碼等等)成名為 dat 的 map/字典對象。這些操作跟其他語言類似,不同的是我們的輸入需要是字節(jié)數(shù)組(不是字符串),對于字典的每個值時需要有類型斷言[2]才能讀取或訪問該值。

          當我們處理一個多層嵌套的 JSON 對象時,這些類型斷言會讓處理變得非常繁瑣。

          解析/序列化為 struct

          第二種處理如下:

          package?main

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

          type?ourData?struct?{
          ????Num???float64?`json:"num"`
          ????Strs?[]string?`json:"strs"`
          ????Obj?map[string]map[string]string?`json:"obj"`
          }

          func?main()?{
          ????byt?:=?[]byte(`{
          ????????"num":6.13,
          ????????"strs":["a","b"],
          ????????"obj":{"foo":{"bar":"zip","zap":6}}
          ????}`
          )

          ????res?:=?ourData{}
          ????json.Unmarshal(byt,?&res)
          ????fmt.Println(res.Num)
          ????fmt.Println(res.Strs)
          ????fmt.Println(res.Obj)
          }

          我們利用 Go struct 的標簽功能把 byt 變量中的字節(jié)反序列化成一個具體的結構 ourData。

          標簽是結構體成員定義后跟隨的字符串。我們的定義如下:

          type?ourData?struct?{
          ????Num???float64?`json:"num"`
          ????Strs?[]string?`json:"strs"`
          ????Obj?map[string]map[string]string?`json:"obj"`
          }

          你可以看到 Num 成員的 JSON 標簽 “num”、Str 成員的 JSON 標簽 “strs”、Obj 成員的 JSON 標簽 “obj”。這些字符串使用反引號[3]把標簽聲明為文字串。除了反引號,你也可以使用雙引號,但是使用雙引號可能會需要一些額外的轉義,這樣看起來會很凌亂。

          type?ourData?struct?{
          ????Num???float64?"json:\"num\""
          ????Strs?[]string?"json:\"strs\""
          ????Obj?map[string]map[string]string?"json:\"obj\""
          }

          在 struct 的定義中,標簽不是必需的。如果你的 struct 中包含了標簽,那么它意味著 Go 的 反射 API[4] 可以訪問標簽的值[5]。Go 中的包可以使用這些標簽來進行某些操作。

          Go 的 encoding/json 包在反序列化 JSON 成員為具體的 struct 時,通過這些標簽來決定每個頂層的 JSON 成員的值。換句話說,當你定義如下的 struct 時:

          type?ourData?struct?{
          ????Num???float64???`json:"num"`
          }

          意味著:

          當使用 json.Unmarshal 反序列化 JSON 對象為這個 struct 時,取它頂層的 num 成員的值并把它賦給這個 struct 的 Num 成員。

          這個操作可以讓你的反序列化代碼稍微簡潔一點,因為程序員不需要對每個成員取值時都顯式地調用類型斷言。然而,這個仍不是最佳解決方案。

          首先 —— 標簽只對頂層的成員有效 —— 嵌套的 JSON 需要對應嵌套的類型(如 Obj map[string]map[string]string),因此繁瑣的操作仍沒有避免。

          其次 —— 它假定你的 JSON 結構不會變化。如果你運行上面的程序,你會發(fā)現(xiàn) "zap":6 并沒有被賦值到 Obj 成員。你可以通過創(chuàng)建類型 map[string]map[string]interface{} 來處理,但是在這里你又需要進行類型斷言了。

          這是我第一個 Go 工程遇到的情況,曾讓我苦不堪言。

          幸運的是,現(xiàn)在我們有了更有效的辦法。

          SJSON 和 GJSON

          Go 內建的 JSON 處理并沒有變化,但是已經(jīng)出現(xiàn)了一些成熟的旨在用起來更簡潔高效的處理 JSON 的包。

          SJSON[6](寫 JSON)和 GJSON[7](讀 JSON)是 Josh Baker[8] 開發(fā)的兩個包,你可以用來讀寫 JSON 字符串。你可以參考 README 來獲取代碼實例 —— 下面是使用 GJSON 從 JSON 字符串中獲取嵌套的值的示例:

          package?main

          import?"github.com/tidwall/gjson"

          const?JSON?=?`{"name":{"first":"Janet","last":"Prichard"},"age":47}`

          func?main()?{
          ????value?:=?gjson.Get(json,?"name.last")
          ????println(value.String())
          }

          類似的,下面是使用 SJSON “設置” JSON 字符串中的值返回設置之后的字符串的示例代碼:

          package?main

          import?"github.com/tidwall/sjson"

          const?JSON?=?`{"name":{"first":"Janet","last":"Prichard"},"age":47}`

          func?main()?{
          ????value,?_?:=?sjson.Set(json,?"name.last",?"Anderson")
          ????println(value)
          }

          如果 SJSON 和 GJSON 不符合你的口味,還有一些[9]其他的[10]第三方庫[11],可以用來在 Go 程序中稍微復雜點地處理 JSON。


          via: https://alanstorm.com/simplified-json-handling-in-go/

          作者:Alan[12]譯者:lxbwolf[13]校對:polaris[14]

          本文由 GCTT[15] 原創(chuàng)編譯,Go 中文網(wǎng)[16] 榮譽推出

          參考資料

          [1]

          Go 實例教程: https://gobyexample.com/json

          [2]

          類型斷言: https://www.sohamkamani.com/golang/type-assertions-vs-type-conversions/

          [3]

          反引號: https://golangbyexample.com/double-single-back-quotes-go/

          [4]

          反射 API: https://pkg.go.dev/reflect

          [5]

          訪問標簽的值: https://stackoverflow.com/questions/23507033/get-struct-field-tag-using-go-reflect-package/23507821#23507821

          [6]

          SJSON: https://github.com/tidwall/sjson

          [7]

          GJSON: https://github.com/tidwall/gjson

          [8]

          Josh Baker: https://github.com/tidwall

          [9]

          一些: https://github.com/pquerna/ffjson

          [10]

          其他的: https://github.com/mailru/easyjson

          [11]

          第三方庫: https://github.com/Jeffail/gabs

          [12]

          Alan: https://alanstorm.com/about/

          [13]

          lxbwolf: https://github.com/lxbwolf

          [14]

          polaris: https://github.com/polaris1119

          [15]

          GCTT: https://github.com/studygolang/GCTT

          [16]

          Go 中文網(wǎng): https://studygolang.com/




          推薦閱讀


          福利

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

          瀏覽 49
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  日韩欧美三级免费 | 91视频网站一区二区三区 | 美女视频黄a视频全免费不卡 | 日日夜夜草 | A∨免费在线观看 |