使用Go語(yǔ)言解析動(dòng)態(tài)JSON格式的方法
通常使用Golang encoding/json 標(biāo)準(zhǔn)庫(kù)可以方便的編碼/解析JSON數(shù)據(jù),但是前提需要定義struct數(shù)據(jù)結(jié)構(gòu)。特別是解析未知結(jié)構(gòu)的JSON數(shù)據(jù)時(shí),原有方法很難滿足需求了,本文主要介紹動(dòng)態(tài)解析JSON格式。
Go語(yǔ)言的JSON 庫(kù)
Go語(yǔ)言自帶的JSON轉(zhuǎn)換庫(kù)為 encoding/json
1.1)其中把對(duì)象轉(zhuǎn)換為JSON的方法(函數(shù))為 json.Marshal(),其函數(shù)原型如下
func Marshal(v interface{}) ([]byte, error)
也就是說(shuō),這個(gè)函數(shù)接收任意類(lèi)型的數(shù)據(jù) v,并轉(zhuǎn)換為字節(jié)數(shù)組類(lèi)型,返回值就是我們想要的JSON數(shù)據(jù)和一個(gè)錯(cuò)誤代碼。當(dāng)轉(zhuǎn)換成功的時(shí)候,這個(gè)錯(cuò)誤代碼為nil
在進(jìn)行對(duì)象轉(zhuǎn)換為 JSON 的過(guò)程中,會(huì)遵循如下幾條規(guī)則:
- 布爾型轉(zhuǎn)換為 JSON 后仍是布爾型 , 如true -> true
- 浮點(diǎn)型和整數(shù)型轉(zhuǎn)換后為JSON里面的常規(guī)數(shù)字,如 1.23 -> 1.23
- 字符串將以UTF-8編碼轉(zhuǎn)化輸出為Unicode字符集的字符串,特殊字符比如<將會(huì)被轉(zhuǎn)義為\u003c
- 數(shù)組和切片被轉(zhuǎn)換為JSON 里面的數(shù)組,[]byte類(lèi)會(huì)被轉(zhuǎn)換為base64編碼后的字符串,slice的零值被轉(zhuǎn)換為null
- 結(jié)構(gòu)體會(huì)轉(zhuǎn)化為JSON對(duì)象,并且只有結(jié)構(gòu)體里邊以大寫(xiě)字母開(kāi)頭的可被導(dǎo)出的字段才會(huì)被轉(zhuǎn)化輸出,而這些可導(dǎo)出的字段會(huì)作為JSON對(duì)象的字符串索引
- 轉(zhuǎn)化一個(gè)map 類(lèi)型的數(shù)據(jù)結(jié)構(gòu)時(shí),該數(shù)據(jù)的類(lèi)型必須是 map[string]T(T 可以是encoding/json 包支持的任意數(shù)據(jù)類(lèi)型)
1.2)把 JSON 轉(zhuǎn)換回對(duì)象的方法(函數(shù))為 json.Unmarshal(),其函數(shù)原型如下
func Unmarshal(data [] byte, v interface{}) error
這個(gè)函數(shù)會(huì)把傳入的 data 作為一個(gè)JSON來(lái)進(jìn)行解析,解析后的數(shù)據(jù)存儲(chǔ)在參數(shù) v 中。這個(gè)參數(shù) v 也是任意類(lèi)型的參數(shù)(但一定是一個(gè)類(lèi)型的指針),原因是我們?cè)谑且源撕瘮?shù)進(jìn)行JSON 解析的時(shí)候,這個(gè)函數(shù)不知道這個(gè)傳入?yún)?shù)的具體類(lèi)型,所以它需要接收所有的類(lèi)型。
那么,在進(jìn)行解析的時(shí)候,如果JSON 和 對(duì)象的結(jié)構(gòu)不對(duì)口會(huì)發(fā)生什么呢,這就需要解析函數(shù)json.Unmarshal()遵循以下規(guī)則
json.Unmarshal() 函數(shù)會(huì)根據(jù)一個(gè)約定的順序查找目標(biāo)結(jié)構(gòu)中的字段,如果找到一個(gè)即發(fā)生匹配。那什么是找到了呢?關(guān)于“找到了”又有如下的規(guī)則:假設(shè)一個(gè)JSON對(duì)象有個(gè)名為"Foo"的索引,要將"Foo"所對(duì)應(yīng)的值填充到目標(biāo)結(jié)構(gòu)體的目標(biāo)字段上,json.Unmarshal() 將會(huì)遵循如下順序進(jìn)行查找匹配
- § 一個(gè)包含F(xiàn)oo 標(biāo)簽的字段
- § 一個(gè)名為Foo 的字段
- § 一個(gè)名為Foo 或者Foo 或者除了首字母其他字母不區(qū)分大小寫(xiě)的名為Foo 的字段。 這些字段在類(lèi)型聲明中必須都是以大寫(xiě)字母開(kāi)頭、可被導(dǎo)出的字段。
注意:如果JSON中的字段在Go目標(biāo)類(lèi)型中不存在,json.Unmarshal() 函數(shù)在解碼過(guò)程中會(huì)丟棄該字段。
當(dāng)JSON 的結(jié)構(gòu)是未知的時(shí)候,會(huì)遵循如下規(guī)則:
- § JSON中的布爾值將會(huì)轉(zhuǎn)換為Go中的bool類(lèi)型
- § 數(shù)值會(huì)被轉(zhuǎn)換為Go中的float64類(lèi)型
- § 字符串轉(zhuǎn)換后還是string類(lèi)型
- § JSON數(shù)組會(huì)轉(zhuǎn)換為[]interface{} 類(lèi)型
- § JSON對(duì)象會(huì)轉(zhuǎn)換為map[string]interface{}類(lèi)型
- § null值會(huì)轉(zhuǎn)換為nil
注意:在Go的標(biāo)準(zhǔn)庫(kù)encoding/json包中,允許使用map[string]interface{}和[]interface{} 類(lèi)型的值來(lái)分別存放未知結(jié)構(gòu)的JSON對(duì)象或數(shù)組
1、傳統(tǒng)方法
比如 User 數(shù)據(jù)結(jié)構(gòu)如下:
type User struct { Name string `json:"name"` Age int `json:"age"` }
在定義struct字段的時(shí)候,可以在字段后面添加tag,來(lái)控制encode/decode的過(guò)程:是否要 decode/encode 某個(gè)字段,JSON 中的字段名稱(chēng)是什么。字段名首字母控制字段的可見(jiàn)性,若要輸出到JSON,首字母需要大寫(xiě)。
三種tag:
-
:不要解析這個(gè)字段
omitempty
:當(dāng)字段為空(默認(rèn)值)時(shí),不要解析這個(gè)字段。比如 false、0、nil、長(zhǎng)度為 0 的 array,map,slice,string
FieldName
:當(dāng)解析 json 的時(shí)候,使用這個(gè)名字
舉例來(lái)說(shuō)吧:
// 解析的時(shí)候忽略該字段。默認(rèn)情況下會(huì)解析這個(gè)字段,因?yàn)樗谴髮?xiě)字母開(kāi)頭的 Field int `json:"-"` // 解析(encode/decode) 的時(shí)候,使用 `other_name`,而不是 `Field` Field int `json:"other_name"` // 解析的時(shí)候使用 `other_name`,如果struct 中這個(gè)值為空,就忽略它 Field int `json:"other_name,omitempty"`
(1)encode
user := User{Name: "test", Age:23} data, err := json.Marshal(user) if err != nil { fmt.Println(string(data)) }
data 就是 []byte 類(lèi)型的數(shù)組,里面包含了解析為 JSON 之后的數(shù)據(jù),可以使用string(data)轉(zhuǎn)型為string。
(2)decode
要把JSON數(shù)據(jù)轉(zhuǎn)換成Go類(lèi)型的值(Decode),可以使用 json.Unmarshal 。
var user User err = json.Unmarshal(data, &user) if err != nil { fmt.Errorf("Can not decode data: %v\n", err) }
2、動(dòng)態(tài)解析
動(dòng)態(tài)JSON結(jié)構(gòu)未知,若使用前面方法需要事先定義數(shù)據(jù)結(jié)構(gòu),這與PHP/Python JSON處理非常不同。若不考慮性能,使用simplejson。
(1)simplejson
js, err := simplejson.NewJson([]byte(`{ "test": { "string_array": ["asdf", "zxcv"], "array": [1, "2", 3], "arraywithsubs": [{"subkeyone": 1}, "bignum": 9223372036854775807, "string": "simplejson", "bool": true } }`)) if err != nil { panic("json format error") } //獲取某個(gè)字段值 s, err := js.Get("test").Get("string").String() if err != nil { panic(err) } fmt.Println(s) //檢查某個(gè)字段是否存在 _, ok := js.Get("test").CheckGet("string2") if ok { fmt.Println("存在!") } else { fmt.Println("不存在") }
(2)interface
比如JSON有以下兩種類(lèi)型:
{"Type":"sound","Msg":{"Description":"dynamite","Authority":"the Bruce Dickinson"}} {"Type":"cowbell","Msg":{"More":true}}
Msg 具體什么類(lèi)型實(shí)現(xiàn)無(wú)法判斷, Msg being a map[string]interface{} :
type Envelope struct { Type string Msg interface{} } var env Envelope if err := json.Unmarshal([]byte(input), &env); err != nil { log.Fatal(err) } // for the love of Gopher DO NOT DO THIS var desc string = env.Msg.(map[string]interface{})["description"].(string) fmt.Println(desc)
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
在 Golang 中實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Http中間件過(guò)程詳解
本文在go web中簡(jiǎn)單的實(shí)現(xiàn)了中間件的機(jī)制,這樣帶來(lái)的好處也是顯而易見(jiàn)的,當(dāng)然社區(qū)也有一些成熟的 middleware 組件,包括 Gin 一些Web框架中也包含了 middleware 相關(guān)的功能,具體內(nèi)容詳情跟隨小編一起看看吧2021-07-07重學(xué)Go語(yǔ)言之運(yùn)算符與控制結(jié)構(gòu)詳解
對(duì)于任何編程語(yǔ)言來(lái)說(shuō),運(yùn)算符和控制結(jié)構(gòu)都算是最基礎(chǔ)的知識(shí)了,既然是基礎(chǔ),當(dāng)然非常有必要學(xué)習(xí),因此在這篇文章中我們就來(lái)討論一下2023-02-02gin項(xiàng)目部署到服務(wù)器并后臺(tái)啟動(dòng)的步驟
本文主要介紹了gin項(xiàng)目部署到服務(wù)器并后臺(tái)啟動(dòng)的步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-02-02Go語(yǔ)言通過(guò)chan進(jìn)行數(shù)據(jù)傳遞的方法詳解
這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言如何通過(guò)chan進(jìn)行數(shù)據(jù)傳遞的功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下2023-06-06Go語(yǔ)言輕量級(jí)高性能嵌入式規(guī)則引擎RuleGo使用詳解
這篇文章主要為大家介紹了Go語(yǔ)言輕量級(jí)高性能嵌入式規(guī)則引擎RuleGo使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11詳解如何在golang鏡像中設(shè)置指定時(shí)區(qū)
這篇文章主要為大家詳細(xì)介紹了如何在golang鏡像中設(shè)置指定時(shí)區(qū),文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的可以了解一下2023-04-04關(guān)于go get 下載第三方包存儲(chǔ)路徑問(wèn)題
這篇文章主要介紹了關(guān)于go get 下載第三方包存儲(chǔ)路徑問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01Go函數(shù)使用(函數(shù)定義、函數(shù)聲明、函數(shù)調(diào)用等)
本文主要介紹了Go函數(shù)使用,包括函數(shù)定義、函數(shù)聲明、函數(shù)調(diào)用、可變參數(shù)函數(shù)、匿名函數(shù)、遞歸函數(shù)、高階函數(shù)等,感興趣的可以了解一下2023-11-11