問題解決:Golang的json包解析欄位失敗

2020-10-23 12:00:47

引言

這個問題出現在解析一個json的組態檔時,排錯了很久,最後和狗勳一起解決了這個奇怪的問題。

正文

其實這個問題並不難,因為Golang自帶json包,所以我們要做的其實就是在需要解析的結構體中加上相應的標籤就可以了,但是可能會出現一些隱性的bug,且沒有任何的報錯。

我一共遇到了兩種問題,其中第一種比較好解決,網上的解決方案也大多是描述第一種問題;但是第二種問題就非常的蹊蹺了,因為在專案中出現了問題,寫了一個測試程式碼並沒有復現成功。

第一種解決方案:結構體需要解析的相應欄位應該大寫

我們先來看一段程式碼:

---test.json
{
  "page": 1,
  "fruits": ["apple", "peach"]
}

---test_json.go
type response struct {
	number int
	Page   int      `json:page`
	fruits []string `json:fruits`
}

func main(){
	data, _ := io.ReadFile("test.json")
	res := response{}
	json.Unmarshal(data, &res)
	fmt.Println(res)
}

ouput:
{0 1 []}

我們可以看到在首字母小寫時出現問題,解決方案也很容易,就是需要把欄位開頭改成大寫,當然這樣是有弊端的,因為一般需要從組態檔中讀取的欄位一般都是與設定相關的,而這些一般應該設定成private的,C++就可以很好的解決,這也許就是go不如C++自由的原因吧,當然這可能也是C++沒有類似go這樣json庫的原因吧。

第二種解決方案:就是Goland的json解析要麼加雙引號,要麼不加雙引號名稱中不能帶下劃線,即_

也就是上面的結構體應該寫成這樣:

type response struct {
	number int
	Page   int      `json:"page"`
	fruits []string `json:"fruits"`
}

也就是這樣子,雖然這個測試程式碼不加雙引號也可以正確解析出slice,但是在專案中的結構體卻出現了問題,我把專案中的結構體中帶json欄位的提取出來就可以復現錯誤了:

---test.json
{
  "servers_address": ["localhost:8901","localhost:8902"],
  "myport" : ":8900",
  "maxreries" : 13,
  "timeout_entry" : 200
}

---test_json.go
type response struct {
	Maxreries      int      `json:maxreries`
	ServersAddress []string `json:servers_address`
	MyPort         string   `json:myport`
	TimeOutEntry   int		`json:timeout_entry`
}

func main(){
	data, _ := io.ReadFile("test.json")
	res := response{}
	json.Unmarshal(data, &res)
	fmt.Println(res)
}

ouput:
{13 [] :8900 0}

我們可以看到解析失敗,ServersAddressTimeOutEntry解析失敗,這已經不是解析slice失敗了,int也出現問題,讓我們加上雙引號看看:

ouput:
{13 [localhost:8901 localhost:8902] :8900 200}

我們可以看到解析成功。

那麼不加雙引號為什麼有時可以解析成功,有時不可以呢?因為已經不是解析slice失敗了,而解析int也同樣出現了錯誤。

觀察兩個出現問題的欄位,發現它們有一個共同點,就是json標籤中都帶了_,讓我們去掉執行看看:

{13 [localhost:8901 localhost:8902] :8900 200}

排查出錯誤!就是Golang的json解析在結構體json標籤不加雙引號時無法解析名稱帶_的欄位

這個問題估計是一個極其隱式的問題,官方檔案中也沒有描述,網上在此之前也沒有一篇文章描述這個問題,希望看完這篇文章能幫助你解決這個令人困惑不以的問題。

參考: