go語言encoding/json標準庫

乾飯人小羽 發佈 2023-12-20T09:03:58.474705+00:00

導入方式:import "encoding/json"json包實現了json對象的編解碼,參見RFC 4627。Json對象和go類型的映射關係請參見Marshal和Unmarshal函數的文檔。參見"JSON and Go"獲取本包的一個介紹:http://golang.

導入方式:

import "encoding/Json"

JSON包實現了json對象的編解碼,參見RFC 4627。Json對象和go類型的映射關係請參見Marshal和Unmarshal函數的文檔。

參見"JSON and Go"獲取本包的一個介紹:http://golang.org/doc/articles/json_and_go.html

funcUnmarshal

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

Unmarshal函數解析json編碼的數據並將結果存入v指向的值。

Unmarshal和Marshal做相反的操作,必要時申請映射、切片或指針,有如下的附加規則:

要將json數據解碼寫入一個指針,Unmarshal函數首先處理json數據是json字面值null的情況。此時,函數將指針設為nil;否則,函數將json數據解碼寫入指針指向的值;如果指針本身是nil,函數會先申請一個值並使指針指向它。

要將json數據解碼寫入一個結構體,函數會匹配輸入對象的鍵和Marshal使用的鍵(結構體欄位名或者它的標籤指定的鍵名),優先選擇精確的匹配,但也接受大小寫不敏感的匹配。

要將json數據解碼寫入一個接口類型值,函數會將數據解碼為如下類型寫入接口:

Bool                   對應JSON布爾類型
float64                對應JSON數字類型
string                 對應JSON字符串類型
[]interface{}          對應JSON數組
map[string]interface{} 對應JSON對象
nil                    對應JSON的null

如果一個JSON值不匹配給出的目標類型,或者如果一個json數字寫入目標類型時溢出,Unmarshal函數會跳過該欄位並儘量完成其餘的解碼操作。如果沒有出現更加嚴重的錯誤,本函數會返回一個描述第一個此類錯誤的詳細信息的UnmarshalTypeError。

JSON的null值解碼為go的接口、指針、切片時會將它們設為nil,因為null在json里一般表示「不存在」。 解碼json的null值到其他go類型時,不會造成任何改變,也不會產生錯誤。

當解碼字符串時,不合法的utf-8或utf-16代理(字符)對不視為錯誤,而是將非法字符替換為unicode字符U+FFFD。

舉例:通過interface{} 與 type assert的配合,這樣就能夠解析未知結構的JSON數了

package main 
import(
    "fmt"
    "encoding/json"
)

func main() {
    var s interface{}
    str := `
{
    "name" : "testJSON",
    "servers" : [
        {
            "serverName" : "Shanghai_VPN",
            "serverIP" : "127.0.0.1"
        },
        {
            "serverName" : "Beijing_VPN",
            "serverIP" : "127.0.0.2"        
        }
    ],
    "status" : false
}`
    json.Unmarshal([]byte(str), &s)
    fmt.Println(s)
    //然後需要通過斷言來將其從interface{}類型轉成map[string]interface{}類型
    m := s.(map[string]interface{})

    for key, value := range m {
        switch v := value.(type){//得到s的類型
        case string :
            fmt.Println(key , " is string ", v)
        case int :
            fmt.Println(key, "is int", v)
        case []interface{}:
            fmt.Println(key, "is an array")
            for i, vv := range v{
                fmt.Println(i, vv)
            }
        default:
            fmt.Println(key, "is of a type i don't know how to handle")
        }
    }
}

返回:

userdeMBP:go-learning user$ go run test.go
map[name:testJSON servers:[map[serverName:Shanghai_VPN serverIP:127.0.0.1] map[serverName:Beijing_VPN serverIP:127.0.0.2]] status:false]
name  is string  testJSON
servers is an array
0 map[serverName:Shanghai_VPN serverIP:127.0.0.1]
1 map[serverIP:127.0.0.2 serverName:Beijing_VPN]
status is of a type i don't know how to handle

funcMarshal

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

Marshal函數返回v的json編碼。

Marshal函數會遞歸的處理值。如果一個值實現了Marshaler接口切非nil指針,會調用其MarshalJSON方法來生成json編碼。nil指針異常並不是嚴格必需的,但會模擬與UnmarshalJSON的行為類似的必需的異常。

否則,Marshal函數使用下面的基於類型的默認編碼格式:

布爾類型編碼為json布爾類型。

浮點數、整數和Number類型的值編碼為json數字類型。

字符串編碼為json字符串。角括號"<"和">"會轉義為"\u003c"和"\u003e"以避免某些瀏覽器吧json輸出錯誤理解為HTML。基於同樣的原因,"&"轉義為"\u0026"。

數組和切片類型的值編碼為json數組,但[]byte編碼為base64編碼字符串,nil切片編碼為null。

結構體的值編碼為json對象。每一個導出欄位變成該對象的一個成員,除非:

- 欄位的標籤是"-"
- 欄位是空值,而其標籤指定了omitempty選項

空值是false、0、""、nil指針、nil接口、長度為0的數組、切片、映射。對象默認鍵字符串是結構體的欄位名,但可以在結構體欄位的標籤里指定。結構體標籤值里的"json"鍵為鍵名,後跟可選的逗號和選項,舉例如下:

// 欄位被本包忽略
Field int `json:"-"`
// 欄位在json里的鍵為"myName"
Field int `json:"myName"`
// 欄位在json里的鍵為"myName"且如果欄位為空值將在對象中省略掉
Field int `json:"myName,omitempty"`
// 欄位在json里的鍵為"Field"(默認值),但如果欄位為空值會跳過;注意前導的逗號
Field int `json:",omitempty"`

"string"選項標記一個欄位在編碼json時應編碼為字符串。它只適用於字符串、浮點數、整數類型的欄位。這個額外水平的編碼選項有時候會用於和javascript程序交互:

Int64String int64 `json:",string"`

如果鍵名是只含有unicode字符、數字、美元符號、百分號、連字符、下劃線和斜槓的非空字符串,將使用它代替欄位名。

匿名的結構體欄位一般序列化為他們內部的導出欄位就好像位於外層結構體中一樣。如果一個匿名結構體欄位的標籤給其提供了鍵名,則會使用鍵名代替欄位名,而不視為匿名。

Go結構體欄位的可視性規則用於供json決定那個欄位應該序列化或反序列化時是經過修正了的。如果同一層次有多個(匿名)欄位且該層次是最小嵌套的(嵌套層次則使用默認go規則),會應用如下額外規則:

1)json標籤為"-"的匿名欄位強行忽略,不作考慮;

2)json標籤提供了鍵名的匿名欄位,視為非匿名欄位;

3)其餘欄位中如果只有一個匿名欄位,則使用該欄位;

4)其餘欄位中如果有多個匿名欄位,但壓平後不會出現衝突,所有匿名欄位壓平;

5)其餘欄位中如果有多個匿名欄位,但壓平後出現衝突,全部忽略,不產生錯誤。

對匿名結構體欄位的管理是從go1.1開始的,在之前的版本,匿名欄位會直接忽略掉。

映射類型的值編碼為json對象。映射的鍵必須是字符串,對象的鍵直接使用映射的鍵。

指針類型的值編碼為其指向的值(的json編碼)。nil指針編碼為null。

接口類型的值編碼為接口內保持的具體類型的值(的json編碼)。nil接口編碼為null。

通道、複數、函數類型的值不能編碼進json。嘗試編碼它們會導致Marshal函數返回UnsupportedTypeError。

Json不能表示循環的數據結構,將一個循環的結構提供給Marshal函數會導致無休止的循環。

funcMarshalIndent

func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)

MarshalIndent類似Marshal但會使用縮進將輸出格式化。其可讀性更高

舉例:

package main 
import(
    "fmt"
    "encoding/json"
    "os"
)
type Address struct {
    City, State string
}
type Person struct {
    Id        int      `json:"id"` //id作為該JSON欄位的key值
    FirstName string    //不設置標籤則默認使用FirstName為欄位key值
    LastName  string   `json:"-"` //欄位被本包忽略,即使有值也不輸出
    Age       int      `json:",omitempty"` //含omitempty選項的欄位如果為空值會省略,如果存在Age作為該JSON欄位的key值
    Height    float32  `json:"height,omitempty"` //含omitempty選項的欄位如果為空值會省略,如果存在height作為該JSON欄位的key值
    Address         //匿名欄位(其標籤無效)會被處理為其欄位是外層結構體的欄位,所以沒有Address這個元素,而是直接顯示City, State這兩個元素
}

func main() {
    v := &Person{Id: 13, FirstName: "John", LastName: "Doe", Age: 42}
    v.Address = Address{"Hanga Roa", "Easter Island"}
    output, err := json.MarshalIndent(v, "&", "    ")//每行以&作為前綴,並縮進四個空格
    if err != nil {
        fmt.Printf("error: %v\n", err)
    }
    os.Stdout.Write(output)
}

返回:

userdeMBP:go-learning user$ go run test.go
{
&    "id": 13,
&    "FirstName": "John",
&    "Age": 42,
&    "City": "Hanga Roa",
&    "State": "Easter Island"
&}

如果使用的是Marshal,則返回:

userdeMBP:go-learning user$ go run test.go
{"id":13,"FirstName":"John","Age":42,"City":"Hanga Roa","State":"Easter Island"}
關鍵字: