成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Go標(biāo)準(zhǔn)庫:Json解析陷阱與版本變動(dòng)時(shí)的偷懶技巧

開發(fā) 后端
日常工作中,最常用的數(shù)據(jù)傳輸格式就是json,而encoding/json庫是內(nèi)置做解析的庫。這一節(jié)來看看它的用法,還有幾個(gè)日常使用中隱晦的陷阱和處理技巧。

 [[414537]]

本文轉(zhuǎn)載自微信公眾號(hào)「機(jī)智的程序員小熊」,作者小熊。轉(zhuǎn)載本文請(qǐng)聯(lián)系機(jī)智的程序員小熊公眾號(hào)。

日常工作中,最常用的數(shù)據(jù)傳輸格式就是json,而encoding/json庫是內(nèi)置做解析的庫。這一節(jié)來看看它的用法,還有幾個(gè)日常使用中隱晦的陷阱和處理技巧。

  • json 與 struct
  • 解析
  • 反解析
  • 陷阱 1、忘記取地址
  • 陷阱 2、大小寫
  • 陷阱 3、十六進(jìn)制或其他非 UTF8 字符串
  • 陷阱 4、數(shù)字轉(zhuǎn) interface{}
  • 神技、版本變更兼容
  • 小結(jié)

json 與 struct

一個(gè)常見的接口返回內(nèi)容如下:

  1.   "data": { 
  2.     "items": [ 
  3.       { 
  4.         "_id": 2 
  5.       } 
  6.     ], 
  7.     "total_count": 1 
  8.   }, 
  9.   "message"""
  10.   "result_code": 200 

在golang中往往是要把json格式轉(zhuǎn)換成結(jié)構(gòu)體對(duì)象使用的。

在新版Goland粘貼json會(huì)自動(dòng)生成結(jié)構(gòu)體,也可以在網(wǎng)上搜到現(xiàn)成的工具完成自動(dòng)轉(zhuǎn)換。

  1. type ResponseData struct { 
  2.  Data struct { 
  3.   Items []struct { 
  4.    Id int `json:"_id"
  5.   } `json:"items"
  6.   TotalCount int `json:"total_count"
  7.  } `json:"data"
  8.  Message    string `json:"message"
  9.  ResultCode int    `json:"result_code"

用反斜杠加注解的方式表明屬于json中哪個(gè)字段,要注意不應(yīng)該嵌套層數(shù)過多,否則難以閱讀容易出錯(cuò)。

一般把內(nèi)部結(jié)構(gòu)體提出來,方便其他業(yè)務(wù)另做他用。

  1. type ResponseData struct { 
  2.  Data struct { 
  3.   Items []Body `json:"items"
  4.   TotalCount int64 `json:"total_count"
  5.  } `json:"data"
  6.  Message    string `json:"message"
  7.  ResultCode int64  `json:"result_code"
  8.  
  9. type Body struct { 
  10.  ID int `json:"_id"

解析

解析就是把json字符串轉(zhuǎn)成struct類型。如下,第一個(gè)參數(shù)為字節(jié)數(shù)組,第二個(gè)為接收的結(jié)構(gòu)體實(shí)體地址。如有報(bào)錯(cuò)返回錯(cuò)誤信息,如沒有返回nil。

  1. //函數(shù)簽名 
  2. func Unmarshal(data []byte, v interface{}) error 
  3. // 用法 
  4. err := json.Unmarshal([]byte(jsonStr), &responseData) 

完整代碼如下

  1. func foo() { 
  2.  jsonStr := `{"data":{"items":[{"_id":2}],"total_count":1},"message":"","result_code":200}` 
  3.  //把string解析成struct 
  4.  var responseData ResponseData 
  5.  err := json.Unmarshal([]byte(jsonStr), &responseData) 
  6.  if err != nil { 
  7.   fmt.Println("parseJson error:" + err.Error()) 
  8.   return 
  9.  } 
  10.  fmt.Println(responseData) 

輸出如下,和java的toString不同,go會(huì)直接輸出了值,如有需要要自行實(shí)現(xiàn)并綁定ToString方法。

  1. {{[{2}] 1}  200} 

反解析

第一步,復(fù)習(xí)初始化結(jié)構(gòu)體的方法。

  1. r := ResponseData{ 
  2.     Data: struct { 
  3.         Items      []Body `json:"items"
  4.         TotalCount int64  `json:"total_count"
  5.     }{ 
  6.         Items: []Body{ 
  7.             {ID: 1}, 
  8.             {ID: 2}, 
  9.         }, 
  10.         TotalCount: 1, 
  11.     }, 
  12.     Message:    ""
  13.     ResultCode: 200, 

如上,無類型的結(jié)構(gòu)體Data需要明確把類型再寫一遍,再為其賦值。[]Body因?yàn)槭橇斜眍愋停瑑?nèi)部如上賦值即可。

反解析函數(shù)簽名如下,傳入結(jié)構(gòu)體,返回編碼好的[]byte,和可能的報(bào)錯(cuò)信息。

  1. func Marshal(v interface{}) ([]byte, error) 

完整代碼如下

  1. func bar() { 
  2.  r := ResponseData{ 
  3.   .... 
  4.  } 
  5.  //把struct編譯成string 
  6.  resBytes, err := json.Marshal(r) 
  7.  if err != nil { 
  8.   fmt.Println("convertJson error: " + err.Error()) 
  9.  } 
  10.  fmt.Println(string(resBytes)) 

輸出

  1. {"data":{"items":[{"_id":1},{"_id":2}],"total_count":1},"message":"","result_code":200} 

陷阱 1、忘記取地址

解析的代碼在結(jié)尾處應(yīng)該是&responseData) 忘記取地址會(huì)導(dǎo)致無法賦值成功,返回報(bào)錯(cuò)。

  1. err := json.Unmarshal([]byte(jsonStr), responseData) 

輸出報(bào)錯(cuò)

  1. json: Unmarshal(non-pointer main.ResponseData) 

陷阱 2、大小寫

定義一個(gè)簡單的結(jié)構(gòu)體來演示這個(gè)陷阱。

  1. type People struct { 
  2.  Name string `json:"name"
  3.  age  int    `json:"age"

變量如果需要被外部使用,也就是java中的public權(quán)限,定義時(shí)首字母必須用大寫,這也是Go約定的權(quán)限控制。

  1. type People struct 

要用來解析json的struct內(nèi)部,假如使用了小寫作為變量名,會(huì)導(dǎo)致無法解析成功,而且不會(huì)報(bào)錯(cuò)!

  1. func err1() { 
  2.  reqJson := `{"name":"minibear2333","age":26}` 
  3.  var person People 
  4.  err := json.Unmarshal([]byte(reqJson), &person) 
  5.  if err != nil {...} 
  6.  fmt.Println(person) 

輸出 0,沒有成功取到age字段。

  1. {minibear2333 0} 

這是因?yàn)闃?biāo)準(zhǔn)庫中是使用反射來獲取的,私有字段是無法獲取到的,源碼內(nèi)部不知道有這個(gè)字段,自然無法顯示報(bào)錯(cuò)信息。

我以前沒有用自動(dòng)解析,手敲上去結(jié)構(gòu)體,很容易出現(xiàn)這樣的問題,把某個(gè)字段首字母弄成小寫。好在編譯器會(huì)有提示。

陷阱 3、十六進(jìn)制或其他非 UTF8 字符串

Go 默認(rèn)使用的字符串編碼是 UTF8 編碼的。直接解析會(huì)出錯(cuò)

  1. func err2() { 
  2.  raw := []byte(`{"name":"\xc2"}`) 
  3.  var person People 
  4.  if err := json.Unmarshal(raw, &person); err != nil { 
  5.   fmt.Println(err) 
  6.  } 

輸出

  1. invalid character 'x' in string escape code 

要特別注意,加上反斜杠轉(zhuǎn)義可以成功,或者使用base64編碼成字符串,這下子單元測(cè)試的重要性就體現(xiàn)出來了。如下:

  1. raw := []byte(`{"name":"\\xc2"}`) 
  2. raw := []byte(`{"name":"wg=="}`) 

其他需要注意的是編碼如果不是UTF-8格式,那么Go會(huì)用 ? (U+FFFD) 來代替無效的 UTF8,這不會(huì)報(bào)錯(cuò),但是獲得的字符串可能不是你需要的結(jié)果。

陷阱 4、數(shù)字轉(zhuǎn) interface{}

因?yàn)槟J(rèn)編碼無類型數(shù)字視為 float64 。如果想用類型判斷語句為int會(huì)直接panic。

  1. func err4() { 
  2.  var data = []byte(`{"age": 26}`) 
  3.  var result map[string]interface{} 
  4.  ... 
  5.  var status = result["age"].(int) //error 
  • 上面的代碼隱含一個(gè)知識(shí)點(diǎn),json中value是簡單類型時(shí),可以直接解析成字典。
  • 如果有嵌套,那么內(nèi)部類型也會(huì)解析成字典。
  • 解析成字典,輸出的時(shí)候有類似ToString的效果。

運(yùn)行時(shí) Panic:

  1. panic: interface conversion: interface {} is float64, not int 
  2.  
  3. goroutine 1 [running]: 
  4. main.err4() 

可以先轉(zhuǎn)換成float64再轉(zhuǎn)換成int

其實(shí)還有幾種方法,太麻煩了也沒有必要,就不做特別介紹了。

神技、版本變更兼容

你有沒有遇到過一種場(chǎng)景,一個(gè)接口更新了版本,把json的某個(gè)字段變更了,在請(qǐng)求的時(shí)候每次都定義兩套struct。

比如Age在版本 1 中是int在版本 2 中是string,解析的過程中就會(huì)出錯(cuò)。

  1. json: cannot unmarshal number into Go struct field People.age of type string 

我在下面介紹一個(gè)技巧,可以省去每次解析都要轉(zhuǎn)換的工作。

我在源碼里面看到,無論反射獲得的是哪種類型都會(huì)去調(diào)用相應(yīng)的解析接口UnmarshalJSON。

結(jié)合前面的知識(shí),在Go里面看起來像鴨子就是鴨子,我們只要實(shí)現(xiàn)這個(gè)方法,并綁定到結(jié)構(gòu)體對(duì)象上,就可以讓源碼來調(diào)用我們的方法。

  1. type People struct { 
  2.     Name string `json:"name"
  3.     Age  int    `json:"_"
  4. func (p *People) UnmarshalJSON(b []byte) error { 
  5.  ... 
  • 使用下劃線表示此類型不解析。
  • 必須用指針的方式綁定方法。
  • 必須與 interface{}中定義的方法簽名完全一致。

一共有四個(gè)步驟

1、定義臨時(shí)類型。用來接受非json:"_"的字段,注意用的是type關(guān)鍵字。

  1. type tmp People 

2、用中間變量接收 json 串,tmp 以外的字段用來接受json:"_"屬性字段

  1. var s = &struct { 
  2.     tmp 
  3.     // interface{}類型,這樣才可以接收任意字段 
  4.     Age interface{} `json:"age"
  5. }{} 
  6. // 解析 
  7. err := json.Unmarshal(b, &s) 

3、判斷真實(shí)類型,并類型轉(zhuǎn)換

  1. switch t := s.Age.(type) { 
  2. case string: 
  3.     var age int 
  4.     age, err = strconv.Atoi(t) 
  5.     if err != nil {...} 
  6.     s.tmp.Age = age 
  7. case float64: 
  8.     s.tmp.Age = int(t) 

4、tmp 類型轉(zhuǎn)換回 People,并賦值

  1. *p = People(s.tmp) 

小結(jié)

通過本節(jié),我們掌握了標(biāo)準(zhǔn)庫中json解析和反解析的方法,以及很有可能日常工作中踩到的幾個(gè)坑。它們是:

  • 陷阱 1、忘記取地址
  • 陷阱 2、大小寫
  • 陷阱 3、十六進(jìn)制或其他非 UTF8 字符串
  • 陷阱 4、數(shù)字轉(zhuǎn) interface{}

版本變量時(shí)兼容技巧

最后分享的技巧在實(shí)際使用中,更加靈活。

留一個(gè)作業(yè):假如有v1和v2不同的兩個(gè)版本json幾乎完成不同,業(yè)務(wù)邏輯已經(jīng)使用v1版本,是否可以把v2版本轉(zhuǎn)換成v1版本,幾乎不用改動(dòng)業(yè)務(wù)邏輯?

提示:可以通過深拷貝把v2版本解析出來的結(jié)構(gòu)體完全轉(zhuǎn)換成v1版本的結(jié)構(gòu)體。

要求:必須使用實(shí)現(xiàn) UnmarshalJSON的技巧。

本文轉(zhuǎn)載自微信公眾號(hào)「機(jī)智的程序員小熊」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系機(jī)智的程序員小熊公眾號(hào)。

 

責(zé)任編輯:武曉燕 來源: 機(jī)智的程序員小熊
相關(guān)推薦

2023-08-28 08:40:23

Sonic開發(fā)JSON

2018-08-02 15:09:20

PyTorch深度學(xué)習(xí)神經(jīng)網(wǎng)絡(luò)

2024-09-29 16:04:14

2024-06-12 08:54:49

Go切片參數(shù)

2024-07-11 08:50:05

Go語言errors

2023-06-26 00:03:55

Go語言類型

2021-07-08 23:53:44

Go語言拷貝

2023-04-02 23:13:07

Go語言bufio

2022-12-07 08:47:48

2021-08-13 12:05:15

Goneturl

2024-06-20 13:38:03

2025-05-12 01:33:00

異步函數(shù)Promise

2009-07-16 09:12:16

程序員偷懶技巧

2010-01-06 16:41:40

解析JSON

2023-07-03 00:44:26

Go語言MySQL

2022-12-02 10:11:50

2010-02-06 14:28:38

C++標(biāo)準(zhǔn)輸入輸出

2025-04-27 09:45:58

JavaScript深拷貝淺拷貝

2024-04-10 08:24:29

2010-01-05 16:41:48

JSON 標(biāo)準(zhǔn)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 精品国产乱码久久久久久丨区2区 | 中文久久 | 久久精品国产一区 | 一区二区三区欧美在线观看 | 亚洲视频在线观看免费 | 欧美精品一二三 | 亚洲美女视频 | 欧美国产免费 | 日韩一级免费电影 | 九九精品久久久 | 欧美xxxⅹ性欧美大片 | 中文字幕精品一区 | 中文字幕一区二区三区四区 | 中文在线亚洲 | 亚洲国产成人av好男人在线观看 | 91在线一区二区三区 | 91av免费看 | 91av在线免费观看 | 免费在线成人 | www.亚洲一区| av网站观看 | 亚洲最大的成人网 | 国产一级视频在线播放 | 国产精品久久久久久吹潮 | 亚洲高清一区二区三区 | 国产在线精品一区二区三区 | av在线免费看网址 | 中文字幕亚洲视频 | 亚洲精品久久久9婷婷中文字幕 | 一区二区久久 | 黄色一级电影在线观看 | 午夜天堂精品久久久久 | 亚洲精品18 | 午夜欧美 | 亚洲精品一区二区在线观看 | 欧美毛片免费观看 | 国产精品色哟哟网站 | 欧美国产一区二区三区 | 国产精品一区二区三区在线 | 中文字幕视频在线观看 | 久久av网 |