Golang JSON 的進階用法

Golang json 的進階用法
痛點
-
你是否遇到過json中某個欄位填入某種型別都適合而陷入兩難境地? (例如:定義了一個port欄位,你卻不知道是填入 8080 ,還是 “8080” 的尷尬局面)
-
你是否遇到過json反解析報錯是因為填入欄位的型別不匹配導致的?例如:
json: cannot unmarshal number into Go struct field Host.port of type string
-
你是否有json某欄位相容2種或者多種的資料結構的需求?
-
你是否想讓程式更優雅,更具有適配性,而不在被這些小細節頭痛?
如果你有或者你想,獲取你可以看看這篇文章。
重現問題
我們給了使用者一個json如下:
{ "name":"yulibaozi", "port":8080 }
但是,業務方卻誤填了”8080”,結果我們程式反解析報錯,導致業務失敗。
json: cannot unmarshal number into Go struct field Host.port of type string
或許你認為這是業務方的問題,但我認為我們可以更優雅的解決這個問題。
如何解決問題
我們先定義了一個結構體
type Host struct { Name string `json:"name"` Port Port`json:"port"` }
心細的你會發現,Port既不是int也不是string型別,而是Port型別,而Port型別是:
type Type int const ( Int Type = iota String ) type Port struct { TypeType IntVal int StrVal string }
在Port結構體中,我們發現了Type型別, 而Type型別包括了int,string兩種型別。接下來就非常重要了,我們需要實現以下這兩個介面。
json.Unmarshaller interface json.Marshaller interface
實現程式碼如下:
type Port struct { TypeType IntVal int StrVal string } // 實現 json.Unmarshaller 介面 func (port *Port) UnmarshalJSON(value []byte) error { if value[0] == '"' { port.Type = String return json.Unmarshal(value, &port.StrVal) } port.Type = Int return json.Unmarshal(value, &port.IntVal) } // 實現 json.Marshaller 介面 func (port Port) MarshalJSON() ([]byte, error) { switch port.Type { case Int: return json.Marshal(port.IntVal) case String: return json.Marshal(port.StrVal) default: return []byte{}, fmt.Errorf("impossible Port.Type") } }
接下來測試:
測試反解析
-
測試反解析int
給出json資料:
{"name":"yulibaozi","port":8090}
反解析得到的結構體資料如下:
&{Name:yulibaozi Port:{Type:0 IntVal:8090 StrVal:}}
-
測試反解析string:
給出json資料:
{"name":"yulibaozi","port":"8090"}
反解析得到的結構體資料如下:
&{Name:yulibaozi Port:{Type:1 IntVal:0 StrVal:8090}}
測試編碼的json
-
測試編碼int的結構體如下:
host := &Host{ Name: "yulibaozi", Port: Port{ Type:Int, IntVal: 8080, }, }
編碼後的json如下:
{"name":"yulibaozi","port":8080}
-
測試編碼string的結構體如下:
host := &Host{ Name: "yulibaozi", Port: Port{ Type:String, StrVal: "8080", }, }
編碼後的json資料如下:
{"name":"yulibaozi","port":"8080"}
在反編碼測試中,你會發現當json填入的型別不同時,會編碼到結構體中對應的欄位中。
在編碼測試中, 具體編碼那個資料是由Type來確定的。
總結
其實,這篇文章只是分享了下json中使用的小技巧,他打破了在使用json時,需要呆板的資料結構的印象,轉而走向了多變,靈活跳脫的風格,其實,這這個小tips的核心在於實現Unmarshaller,Marshaller這兩個結構體,他們的實現是這個分享的關鍵,當然,你可以實現如開篇所說的那樣,json某欄位相容2種及以上結構,當然,你也可以對yaml,toml等進行折騰,都會得到你想要的答案。
編輯:Ghoul