Go解析不定JSON數(shù)據(jù)的方法詳解
前言
在開發(fā)中常常會碰到很多JSON類型的數(shù)據(jù)進行交互,而其中有很多JSON數(shù)據(jù)你是不能確定它的字段和結(jié)構(gòu)的,而Go語言是一門靜態(tài)強類型的語言,在進行JSON解析的時候必須要確定字段的類型,定義出對應(yīng)的結(jié)構(gòu)體,然后再進行Unmarshal,那這二者之間的沖突我們該如何解決呢?
什么是JSON
- json是JavaScript Object Notation(JavaScript對象表示法)
- json是輕量級的文本數(shù)據(jù)交換格式
- json獨立于語言
- json具有自我描述性,更容易理解
- json使用js語法來描述數(shù)據(jù)對象,但是json仍然獨立于語言和平臺,json解析器和json庫支持許多不同的編程語言
json是一種輕量級的數(shù)據(jù)交換格式,易于人閱讀和編寫,同時也易于機器解析和生成,之所以json這么流行,是因為json的結(jié)構(gòu)和多級結(jié)構(gòu)體(對象)剛好能對應(yīng)上,并且本身也十分易讀。而前后端交互的時候后端通常會返回給前端一個多級的結(jié)構(gòu)體,于是json慢慢開始流行了,且json是跨語言和跨平臺的,自身也足夠輕量級。
json的幾種標(biāo)準格式
一個標(biāo)準的json數(shù)據(jù) //每個key對應(yīng)的是一個value { “k1": 1, "k2": 2 //注意結(jié)尾的這個不能有逗號 } json字符串 { "k1": "1", "k2": "2" } json數(shù)組 { “k1”: [1,2], “k2”: [3,4] } json對象 { “k1”: {“1”: “haihai”}, “k2”: {“2”:”haihahai”} } json對象數(shù)組 { “k1”: [ {“k11”: “hellohello”}, {“k12”: “badbad”} ] } json數(shù)組對象 { “k2”: { “hello”: [1,2,3] } } 所有的JSON數(shù)據(jù)都是由上述幾種JSON數(shù)據(jù)組合而成
如何在Go中解析不確定的JSON數(shù)據(jù)
通過看文檔的方式去確定對應(yīng)的JSON數(shù)據(jù),然后構(gòu)造對應(yīng)的結(jié)構(gòu)體
這是最靠譜的方式,最合理也是效率最高的方式。
// 請求其他服務(wù) jsonStr := xxx var data interface{} err := json.Unmarshal([]byte(jsonStr),&data) fmt.Println(data)
比如可以先拿一個interface{}類型來接住JSON數(shù)據(jù),然后看這個interface{}的值,來確定這個JSON數(shù)據(jù)哪些字段是string 哪些是object 哪些是int float等等
當(dāng)然這也不是完全適用的,比如下面這種情況,有一個字段如下
type : []
能看出來type是一個切片類型的值,但是具體的類型你并不知道,可能是[]int 也有可能是[]string []float等等
map[string] interface{}
這個類型是map鍵值對,值可以是任意類型,因為在go中任意類型都實現(xiàn)了空接口interface{},而json數(shù)據(jù)也是key value的鍵值對,所以map[string] interface{}天然支持解析json類型數(shù)據(jù)
jsonStr := xxx var data map[string]interface{} err := json.Unmarshal([]byte(jsonStr),&data) // 你想取的字段 fieldValue := data["field"] // 類型斷言 if value,ok := data["field"].(float64);ok { } else if vluae,ok := data["field"].(int64); ok { } 理論上所有的合法的JSON數(shù)據(jù)都可以被反序列化到map[string]interface{}中 但是實際應(yīng)用中 可能會出現(xiàn)一些無法被map[string]interface{}解析的JSON數(shù)據(jù)
- JSON 數(shù)據(jù)中包含了多層嵌套的數(shù)據(jù)結(jié)構(gòu)。在這種情況下,如果沒有使用遞歸或者其他方式對嵌套數(shù)據(jù)進行處理,可能會導(dǎo)致反序列化失敗。
- JSON 數(shù)據(jù)中包含了數(shù)組類型,但是數(shù)組元素類型不一致或者無法轉(zhuǎn)換成相應(yīng)的類型。在這種情況下,可能需要手動處理數(shù)組元素或者使用其他數(shù)據(jù)類型來保存數(shù)組數(shù)據(jù)。
- JSON 數(shù)據(jù)中包含了自定義數(shù)據(jù)類型或者復(fù)雜的數(shù)據(jù)結(jié)構(gòu),無法使用 map[string]interface{} 類型來反序列化。在這種情況下,需要定義相應(yīng)的結(jié)構(gòu)體或者使用其他適合的數(shù)據(jù)類型來反序列化。
第三方庫
除了encoding/json之外,還有很多第三方庫可以用來解析不確定的JSON數(shù)據(jù),例如gjson和jsonparser,這些庫通常提供了更加靈活和高效的JSON解析方式,可以根據(jù)具體的需求選擇合適的庫來使用
json.RawMessage與json.Number
- json.RawMessage 是一個非常高效的數(shù)據(jù)類型,因為她不需要進行任何解析和類型轉(zhuǎn)換,直接保存了未經(jīng)處理的原始JSON數(shù)據(jù),在反序列化的時候只需要將
json.RawMessage
轉(zhuǎn)化為對應(yīng)的數(shù)據(jù)類型即可,無需重新解析JSON數(shù)據(jù) - json.Number 表示JSON中的數(shù)字類型,可以用來保存任意精度的數(shù)字。這個數(shù)字可以特別大,可能會無法用Go中的整數(shù)或者浮點數(shù)來表示
package main import ( "encoding/json" "fmt" ) func main() { jsonData := []byte(`{ "id": 12345, "name": "John Doe", "age": 30, "score": 95.5, "is_student": true, "tags": ["tag1", "tag2", "tag3"], "extra": { "field1": "value1", "field2": 123 } }`) var m map[string]json.RawMessage err := json.Unmarshal(jsonData, &m) if err != nil { panic(err) } var id int err = json.Unmarshal(m["id"], &id) if err != nil { panic(err) } fmt.Printf("id: %d\n", id) var name string err = json.Unmarshal(m["name"], &name) if err != nil { panic(err) } fmt.Printf("name: %s\n", name) var age int err = json.Unmarshal(m["age"], &age) if err != nil { panic(err) } fmt.Printf("age: %d\n", age) var score float64 err = json.Unmarshal(m["score"], &score) if err != nil { panic(err) } fmt.Printf("score: %f\n", score) var isStudent bool err = json.Unmarshal(m["is_student"], &isStudent) if err != nil { panic(err) } fmt.Printf("is_student: %v\n", isStudent) var tags []string err = json.Unmarshal(m["tags"], &tags) if err != nil { panic(err) } fmt.Printf("tags: %v\n", tags) var extra map[string]json.RawMessage err = json.Unmarshal(m["extra"], &extra) if err != nil { panic(err) } var field1 string err = json.Unmarshal(extra["field1"], &field1) if err != nil { panic(err) } fmt.Printf("extra.field1: %s\n", field1) var field2 int err = json.Unmarshal(extra["field2"], &field2) if err != nil { panic(err) } fmt.Printf("extra.field2: %d\n", field2) } // 不確定的類型 data := make(map[string]interface{}) if err := json.Unmarshal(rawData, &data); err != nil { log.Fatal(err) } if value, ok := data["age"].(float64); ok { // 處理年齡為浮點數(shù)的情況 } else if value, ok := data["age"].(int); ok { // 處理年齡為整數(shù)的情況 } else { // 處理年齡為其他類型或不存在的情況 }
需要注意的是:類型斷言的底層為反射,因為在運行時需要判斷一個接口值的具體類型,而這個類型是在編譯時無法確定的,需要在運行時動態(tài)地獲取。效率比正常的代碼低一到兩個數(shù)量級,而且需要消耗額外的時間和內(nèi)存。
最后
以上就是Go解析不定JSON數(shù)據(jù)的方法詳解的詳細內(nèi)容,更多關(guān)于Go解析不定JSON數(shù)據(jù)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Golang標(biāo)準庫os/exec執(zhí)行外部命令并獲取其輸出包代碼示例
這篇文章主要為大家介紹了Golang標(biāo)準庫os/exec執(zhí)行外部命令并獲取其輸出包代碼示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12Golang算法問題之?dāng)?shù)組按指定規(guī)則排序的方法分析
這篇文章主要介紹了Golang算法問題之?dāng)?shù)組按指定規(guī)則排序的方法,結(jié)合實例形式分析了Go語言數(shù)組排序相關(guān)算法原理與操作技巧,需要的朋友可以參考下2017-02-02Go中g(shù)routine通信與context控制實例詳解
隨著context包的引入,標(biāo)準庫中很多接口因此加上了context參數(shù),下面這篇文章主要給大家介紹了關(guān)于Go中g(shù)routine通信與context控制的相關(guān)資料,需要的朋友可以參考下2022-02-02詳解如何使用Golang操作MongoDB數(shù)據(jù)庫
在現(xiàn)代開發(fā)中,數(shù)據(jù)存儲是一個至關(guān)重要的環(huán)節(jié),MongoDB作為一種NoSQL數(shù)據(jù)庫,提供了強大的功能和靈活的數(shù)據(jù)模型,與Golang的高性能和并發(fā)性能非常契合,本文將探討Golang與MongoDB的完美組合,介紹如何使用Golang操作MongoDB數(shù)據(jù)庫,需要的朋友可以參考下2023-11-11