天天看點

GO語言JSON

JSON設計原理

Go 語言通過 encoding/json 對外提供标準的 JSON 序列化和反序列化方法,即 encoding/json.Marshal 和 encoding/json.Unmarshal,它們也是包中最常用的兩個方法。

序列化和反序列化

序列化和反序列化的開銷完全不同,JSON 反序列化的開銷是序列化開銷的好幾倍,相信這背後的原因也非常好了解。Go 語言中的 JSON 序列化過程不需要被序列化的對象預先實作任何接口,它會通過反射擷取結構體或者數組中的值并以樹形的結構遞歸地進行編碼,标準庫也會根據 encoding/json.Unmarshal 中傳入的值對 JSON 進行解碼。

接口

JSON 标準庫中提供了 encoding/json.Marshaler 和 encoding/json.Unmarshaler 兩個接口分别可以影響 JSON 的序列化和反序列化結果:

type Marshaler interface {

MarshalJSON() ([]byte, error)}

type Unmarshaler interface {

UnmarshalJSON([]byte) error}

除了這兩個方法之外,Go 語言其實還提供了另外兩個用于控制編解碼結果的方法,即 encoding.TextMarshaler 和 encoding.TextUnmarshaler

type TextMarshaler interface {

MarshalText() (text []byte, err error)}

type TextUnmarshaler interface {

UnmarshalText(text []byte) error}

标簽

Go 語言的字段一般都是駝峰命名法,JSON 中下劃線的命名方式相對比較常見,使用标簽這一特性直接建立鍵與字段之間的映射關系

常見的兩個标簽是 string 和 omitempty,前者表示目前的整數或者浮點數是由 JSON 中的字元串表示的,而另一個字段 omitempty 會在字段為零值時,直接在生成的 JSON 中忽略對應的鍵值對,例如:“age”: 0、“author”: “” 等。标準庫會使用如下所示的 encoding/json.parseTag 來解析标簽:

JSON 标準庫中的合法标簽是什麼形式的:标簽名和标簽選項都以 , 連接配接,最前面的字元串為标簽名,後面的都是标簽選項。

序列化

encoding/json.Marshal 是 JSON 标準庫中提供的最簡單的序列化函數,它會接收一個 interface{} 類型的值作為參數

它的實作:

func Marshal(v interface{}) ([]byte, error) {

e := newEncodeState()

err := e.marshal(v, encOpts{escapeHTML: true})

if err != nil {

return nil, err

}

buf := append([]byte(nil), e.Bytes()…)

encodeStatePool.Put(e)

return buf, nil}

上述方法會調用 encoding/json.newEncodeState 從全局的編碼狀态池中擷取 encoding/json.encodeState,随後的序列化過程都會使用這個編碼狀态,該結構體也會在編碼結束後被重新放回池中以便重複利用。

反序列化

标準庫會使用 encoding/json.Unmarshal 處理 JSON 的反序列化,與執行過程确定的序列化相比,反序列化的過程是逐漸探索的過程,是以會複雜很多,開銷也會高出幾倍。

在真正執行反序列化之前,我們會先調用 encoding/json.checkValid 驗證傳入 JSON 的合法性保證在反序列化的過程中不會遇到文法錯誤的問題,在通過合法性的驗證之後,标準庫會初始化資料并調用 encoding/json.decodeState.unmarshal 開始反序列化

JSON 本身就是一種樹形的資料結構,無論是序列化還是反序列化,都會遵循自頂向下的編碼和解碼過程,使用遞歸的方式處理 JSON 對象。