這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
JSON(JavaScript Object Notation)是一種比XML更輕量級的資料交換格式,在易於人們閱讀和編寫的同時,也易於程式解析和產生。儘管JSON是JavaScript的一個子集,但JSON採用完全獨立於程式設計語言的文字格式設定,且表現為key/value的文本描述形式(與GO語言中的map極為相似),這使它成為較理想的、跨平台的、跨語言的資料交換語言。記得初次接觸JSON這種資料形式是在剛工作時,當時在js頁面中處理非常方便,印象一直很深刻。後來,學習了一些mongodb,完全處理這種格式的資料。原來,個人很不喜歡像這種鍵-值對的資料形式,因為總覺得這個底層可能映射起來比較方便,不過,好多情況下確實是少不了這種形式。
Go語言內建對JSON的支援,使用GO語言內建的encoding/json標準庫,開發人員可以便於使用GO程式產生和解析JSON格式的資料。
舉個例子:
package mainimport ( "encoding/json" "fmt")type Book struct { Title string Author []string Publisher string Price float64 IsPublished bool}func main() { b := []byte(`{ "Title":"go programming language", "Author":["john","ada","alice"], "Publisher":"qinghua", "IsPublished":true, "Price":99 }`)//先建立一個目標類型的執行個體對象,用於存放解碼後的值 var book Book err := json.Unmarshal(b, &book) if err != nil { fmt.Println("error in translating,", err.Error()) return } fmt.Println(book.Author)}
Json.Unmarshal()函數會根據一個約定的順序尋找目標結構中的欄位,如果找到一個則進行匹配。這些欄位在型別宣告中必須都是以大寫字母開頭、可被匯出的欄位。
但是當JSON資料中的資料結構和GO中的目標類型不符時,會怎樣呢?如果JSON中的欄位在GO目標類型中不存在,json.Unmarshal()函數在解碼過程中會丟棄該欄位。這個特性讓我們可以從一段JSON資料中篩選出指定的值填充到多個GO語言類型中,當然,前提是已知JSON資料的欄位結構。這也意味著,目標類型中不可被匯出的私人欄位(非首字母大寫)將不會受到解析轉化的影響,
如果要解析一個設定檔,為了使程式端的改動能夠靈活,大多數情況下是不知道設定檔中到底是怎樣的結構,只是在需要的時候去取就可以了。GO語言支援介面。在go中,介面是一組預定義方法的組合,任何一個類型均可通過實現介面預定義的方法來實現,且無需顯示聲明,所以沒有任何方法的空介面代表任何類型。換句話說,每一個類型其實都至少實現了一個空介面。GO內建這樣靈活的類型系統,向我們傳達了一個很有價值的資訊:空介面是通用類型。如果要解析一段未知結構的JSON,只需向這段JSON資料解碼輸出到一個空介面即可。在GO的標準庫encoding/json包中,允許使用map[string]interface{}和[]interface{}類型的值來分別存放未知結構的JSON對象或數組。
還是剛才的例子,只是將結構體去掉,變成一個未知結構的資料。
package mainimport ( "encoding/json" "fmt")func main() { b := []byte(`{ "Title":"go programming language", "Author":["john","ada","alice"], "Publisher":"qinghua", "IsPublished":true, "Price":99 }`) //先建立一個目標類型的執行個體對象,用於存放解碼後的值 var inter interface{} err := json.Unmarshal(b, &inter) if err != nil { fmt.Println("error in translating,", err.Error()) return } //要訪問解碼後的資料結構,需要先判斷目標結構是否為預期的資料類型 book, ok := inter.(map[string]interface{}) //然後通過for迴圈一一訪問解碼後的目標資料 if ok { for k, v := range book { switch vt := v.(type) { case float64: fmt.Println(k, " is float64 ", vt) case string: fmt.Println(k, " is string ", vt) case []interface{}: fmt.Println(k, " is an array:") for i, iv := range vt { fmt.Println(i, iv) } default: fmt.Println("illegle type") } } }}
說實話,這樣轉化確實很繁瑣,但也是解析未知結構的JSON資料的方式。
原來試圖使用XML來做設定檔,但是,那樣的話必須要知道有哪些資料項目(Key),而對於靈活的設定檔來說,到底包括哪些是沒有辦法提前預知的,而各個模組需要哪些資料的時候只需到公用資訊中去提取就好了,主程式也不需要知道到底有哪些資訊是公用的,它只要負責將訊息以索引值對的形式儲存起來供子程式調用就可以了。