使用
Golang 的 encoding/json
庫已經提供了很好的封裝,可以讓我們很方便地進行 JSON 數據的轉換。
Go 語言中數據結構和 JSON 類型的對應關係如下表:
golang 類型 | JSON 類型 | 注意事項 |
---|---|---|
bool | JSON booleans | |
浮點數、整數 | JSON numbers | |
字符串 | JSON strings | 字符串會轉換成 UTF-8 進行輸出,無法轉換的會打印對應的 unicode 值。而且爲了防止瀏覽器把 json 輸出當做 html, “<”、”>” 以及 “&” 會被轉義爲 “\u003c”、”\u003e” 和 “\u0026”。 |
array,slice | JSON arrays | []byte 會被轉換爲 base64 字符串,nil slice 會被轉換爲 JSON null |
struct | JSON objects | 只有導出的字段(以大寫字母開頭)纔會在輸出中 |
NOTE:Go 語言中一些特殊的類型,比如 Channel、complex、function 是不能被解析成 JSON 的。
Encode 和 Decode
要把 golang 的數據結構轉換成 JSON 字符串(encode),可以使用 Marshal
函數:
func Marshal(v interface{}) ([]byte, error)
比如我們有結構體 User
type User struct {
Name string
IsAdmin bool
Followers uint
}
以及一個實例:
user := User{
Name: "cizixs",
IsAdmin: true,
Followers: 36,
}
data, err := json.Marshal(user)
那麼 data 就是 []byte
類型的數組,裏面包含了解析爲 JSON 之後的數據:
data == []byte(`{"Name":"cizixs","IsAdmin":true,"Followers":36}`)
相對應的,要把 JSON 數據轉換成 Go 類型的值(Decode), 可以使用 json.Unmarshal
。它的定義是這樣的:
func Unmarshal(data []byte, v interface{}) error
data 中存放的是 JSON 值,v 會存放解析後的數據,所以必須是指針,可以保證函數中做的修改能保存下來。
下面看個例子:
data = []byte(`{"Name":"gopher","IsAdmin":false,"Followers":8900}`)
var newUser = new(User)
err = json.Unmarshal(data, &newUser)
if err != nil {
fmt.Errorf("Can not decode data: %v\n", err)
}
fmt.Printf("%v\n", newUser)
那麼 Unmarshal
是怎麼找到結構體中對應的值呢?比如給定一個 JSON key Filed
,它是這樣查找的:
- 首先查找 tag 名字(關於 JSON tag 的解釋參看下一節)爲
Field
的字段 - 然後查找名字爲
Field
的字段 - 最後再找名字爲
FiElD
等大小寫不敏感的匹配字段。 - 如果都沒有找到,就直接忽略這個 key,也不會報錯。這對於要從衆多數據中只選擇部分來使用非常方便。
更多控制:Tag
在定義 struct 字段的時候,可以在字段後面添加 tag,來控制 encode/decode 的過程:是否要 decode/encode 某個字段,JSON 中的字段名稱是什麼。
可以選擇的控制字段有三種:
-
:不要解析這個字段omitempty
:當字段爲空(默認值)時,不要解析這個字段。比如 false、0、nil、長度爲 0 的 array,map,slice,stringFieldName
:當解析 json 的時候,使用這個名字
舉例來說吧:// 解析的時候忽略該字段。默認情況下會解析這個字段,因爲它是大寫字母開頭的 Field int `json:"-"` // 解析(encode/decode) 的時候,使用 `other_name`,而不是 `Field`Field int `json:"other_name"` // 解析的時候使用 `other_name`,如果struct 中這個值爲空,就忽略它 Field int `json:"other_name,omitempty"`
由於原文篇幅較長,這裏只轉載一部分,可去原文查看全部內容http://cizixs.com/2016/12/19/golang-json-guide
自己寫幾個小demo測試一下:
package main import ( "encoding/json" "fmt" ) type peerInfo struct { HTTPPort int TCPPort int versiong string } func main() { pi := peerInfo{80, 3306, "0.0.1"} js, err := json.Marshal(pi) if err != nil { fmt.Println(err) } fmt.Println(string(js)) }
輸出結果:
{"HTTPPort":80,"TCPPort":3306}
首字母爲小寫時,爲private字段,不會轉換。
struct中添加tag:
package main import ( "encoding/json" "fmt" ) type peerInfo struct { HTTPPort int `json:"http_port"` TCPPort int `json:"tcp_port"` versiong string } func main() { pi := peerInfo{80, 3306, "0.0.1"} js, err := json.Marshal(pi) if err != nil { fmt.Println(err) } fmt.Println(string(js)) }
輸出結果:
{"http_port":80,"tcp_port":3306}
json轉換成go對象:
package main import ( "encoding/json" "fmt" ) type peerInfo struct { HTTPPort int `json:"http_port"` TCPPort int `json:"tcp_port"` versiong string } func main() { var v peerInfo data := []byte(`{"http_port":80,"tcp_port":3306}`) err := json.Unmarshal(data, &v) if err != nil { fmt.Println(err) } fmt.Printf("%+v\n", v) }
輸出結果:
{HTTPPort:80 TCPPort:3306 versiong:}
去除tag:
package main import ( "encoding/json" "fmt" ) type peerInfo struct { HTTPPort int TCPPort int versiong string } func main() { var v peerInfo data := []byte(`{"http_port":80,"tcp_port":3306}`) err := json.Unmarshal(data, &v) if err != nil { fmt.Println(err) } fmt.Printf("%+v\n", v) }
得到結果:
{HTTPPort:0 TCPPort:0 versiong:}
無法解析到對象中。
最後,再貼一下原文中關於json.Unmarshal的幾點:
- 首先查找 tag 名字(關於 JSON tag 的解釋參看下一節)爲
Field
的字段 - 然後查找名字爲
Field
的字段 - 最後再找名字爲
FiElD
等大小寫不敏感的匹配字段。 - 如果都沒有找到,就直接忽略這個 key,也不會報錯。