本篇重點會著重於Go如何解析JSON,以及了解Go自有的 gob 二進位編碼功能。
使用 Go 標準函式庫 encoding/json Unmarshal()函式
func Unmarshal(data []byte, v interface{}) error
參數 data([]byte 切片)就是儲存 JSON 資料的字串,
而 v 就是用來儲存解析結果的變數,它型別為空介面,我們會傳入一個結構指標。
Unmarshal()會解析JSON字串並嘗試存入到該結構中, v 不能為 nil,否則會跳出錯誤 :
call Unmarshal passes non-pointer as second argument
範例 :
package main
import (
"encoding/json"
"fmt"
)
type greeting struct { //用來儲存解碼的JSON資料的結構
Message string
}
func main() {
//JSON
data := []byte(`
{
"message":"Hello Gopher !"
}
`)
var v greeting // 建立一個空結構
err := json.Unmarshal(data, &v) // 解析 JSON 和寫入 v
if err != nil {
fmt.Println(err)
}
fmt.Println(v) //印出c內容
}
執行結果:
變成greeting結構
結構的欄位必須是可以匯出的(exportable),也就是字首英文大寫,才能被Unmarshal()看到!!
當我們希望將 JSON 資料解碼到 Go 的結構時,我們可以使用結構標籤(struct tags)來告知 Unmarshal 函數如何將 JSON 中的 key 映射到結構的字段。這在 JSON 的 key 名稱和 Go 結構的字段名稱不一致時特別有用。
package main
import (
"encoding/json"
"fmt"
"os"
)
type Person struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Age int `json:"age"`
}
func main() {
data := []byte(`{
"first_name": "John",
"last_name": "Doe",
"age": 30
}`)
if !json.Valid(data) {
fmt.Printf("JSON格式無效: %s", data)
os.Exit(1)
}
var p Person
if err := json.Unmarshal(data, &p); err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Decoded struct: %+v\n", p)
}
在上述範例中,Person 結構有三個字段,每個字段後面都有結構標籤。這些標籤指示 json.Unmarshal 函數如何將 JSON 資料的 key 映射到結構的對應字段。
執行結果:
Decoded struct: {FirstName:John LastName:Doe Age:30}
Unmarshal()會根據以下規則來決定要把JSON的鍵配對到哪一個結構欄位 :
一個 JSON 複合結構 :
{
"lname":"Ricky",
"fname":"Hsieh",
"address": {
"street" : "ABC st",
"city" : "Taipei",
"state" : "TW"
}
}
同樣的我們需要複合struct :
package main
import (
"encoding/json"
"fmt"
"os"
)
type Person struct {
FirstName string `json:"fname"`
LastName string `json:"lname"`
Address address `json:"address"` //子結構
}
type address struct {
Street string `json:"street"`
City string `json:"city"`
State string `json:"state"` //子結構
}
func main() {
data := []byte(`{
"lname":"Ricky",
"fname":"Hsieh",
"address": {
"street" : "ABC st",
"city" : "Taipei",
"state" : "TW"
}
}`)
if !json.Valid(data) {
fmt.Printf("JSON格式無效: %s", data)
os.Exit(1)
}
var p Person
if err := json.Unmarshal(data, &p); err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Decoded struct: %+v\n", p)
}
執行結果 :
以下是一個更複雜的 JSON 範例,其中包含了一個 contacts 陣列(slice),每一個聯絡人都有姓名和地址等資料:
{
"company": "TechCorp",
"contacts": [
{
"lname": "Wang",
"fname": "Li",
"address": {
"street": "123 Main St",
"city": "Beijing",
"state": "BJ"
}
},
{
"lname": "Chen",
"fname": "Yu",
"address": {
"street": "456 West St",
"city": "Shanghai",
"state": "SH"
}
}
]
}
要解析上述 JSON,需要擴展 Go 程式碼來包含這個新的結構:
type Company struct {
Name string `json:"company"`
Contacts []Person `json:"contacts"`
}
完整程式碼一覽 :
package main
import (
"encoding/json"
"fmt"
"os"
)
type Person struct {
FirstName string `json:"fname"`
LastName string `json:"lname"`
Address address `json:"address"` //子結構
}
type Company struct {
Name string `json:"company"`
Contacts []Person `json:"contacts"`
}
type address struct {
Street string `json:"street"`
City string `json:"city"`
State string `json:"state"` //子結構
}
func main() {
data := []byte(`{
"company": "TechCorp",
"contacts": [
{
"lname": "Wang",
"fname": "Li",
"address": {
"street": "123 Main St",
"city": "Beijing",
"state": "BJ"
}
},
{
"lname": "Chen",
"fname": "Yu",
"address": {
"street": "456 West St",
"city": "Shanghai",
"state": "SH"
}
}
]
}
`)
if !json.Valid(data) {
fmt.Printf("JSON格式無效: %s", data)
os.Exit(1)
}
var c Company
if err := json.Unmarshal(data, &c); err != nil {
fmt.Println("Error:", err)
return
}
fmt.Printf("Decoded struct: %+v\n", c)
}
現在我們要將Go結構轉為JSON。
Marshal()功用就是和Unmarshal()相反
func Marshal(v interface{}) ([]byte, error)
參數 v 需要編碼成JSON格式的原始資料,通常是個結構。
Marshal()會回傳JSON字串([]byte切片)以及error值,如果編碼失敗 error 就不為nil。
簡單的範例 :
package main
import (
"encoding/json"
"fmt"
)
// 定義一個 Person 結構
type Person struct {
FirstName string `json:"fname"`
LastName string `json:"lname"`
Age int `json:"age"`
}
func main() {
// 創建一個 Person 實例
p := Person{
FirstName: "John",
LastName: "Doe",
Age: 30,
}
// 使用 Marshal 函式將 Person 結構轉為 JSON 格式
jsonData, err := json.Marshal(p)
if err != nil {
fmt.Println("Error marshaling:", err)
return
}
// 輸出 JSON 數據
fmt.Println(string(jsonData))
}
輸出結果 :
{"fname":"John","lname":"Doe","age":30}
json輸出結果太亂可以用 : Json Formatter
Marshal() 的解析結構規則,會遵循以下來產生JSON鍵值對 :
來看以下範例 :
package main
import (
"encoding/json"
"fmt"
)
// Person 結構,其中包含一個 Address 欄位結構
type Person struct {
FirstName string `json:"fname"`
LastName string `json:"lname"`
Age int `json:"age"`
Addresses []Address `json:"addresses"` // 注意這是一個切片,代表一個人可能有多個地址
}
// Address 結構,表示地址
type Address struct {
Street string `json:"street"`
City string `json:"city"`
State string `json:"state"`
ZipCode string `json:"zip"`
}
func main() {
// 創建一個 Person 實例,其中包含兩個地址
p := Person{
FirstName: "John",
LastName: "Doe",
Age: 30,
Addresses: []Address{
{
Street: "123 Elm St",
City: "Somewhere",
State: "CA",
ZipCode: "90001",
},
{
Street: "456 Oak St",
City: "Nowhere",
State: "TX",
ZipCode: "75001",
},
},
}
// 使用 Marshal 函式將 Person 結構轉為 JSON 格式
jsonData, err := json.Marshal(p)
if err != nil {
fmt.Println("Error marshaling:", err)
return
}
// 輸出 JSON 數據
fmt.Println(string(jsonData))
}
輸出結果 :
{
"fname": "John",
"lname": "Doe",
"age": 30,
"addresses": [
{
"street": "123 Elm St",
"city": "Somewhere",
"state": "CA",
"zip": "90001"
},
{
"street": "456 Oak St",
"city": "Nowhere",
"state": "TX",
"zip": "75001"
}
]
}
如果我們沒有JSON資料直接值型程式 :
{
"fname": "",
"lname": "",
"age": 0,
"addresses": null
}
如果希望某個欄位沒有賦值就不要編碼到JSON資料中,就再欄位標籤後再加上omitempty
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
FirstName string `json:"fname"`
LastName string `json:"lname"`
Age int `json:"age,omitempty"` // 使用 omitempty,如果 Age 為0,則不會被編碼到 JSON 中
Email string `json:"email,omitempty"` // 使用 omitempty,如果 Email 為空字符串,則不會被編碼到 JSON 中
Addresses []Address `json:"addresses,omitempty"` // 使用 omitempty,如果 Addresses 為空切片或 nil,則不會被編碼到 JSON 中
}
type Address struct {
Street string `json:"street"`
City string `json:"city"`
State string `json:"state"`
ZipCode string `json:"zip,omitempty"` // 使用 omitempty,如果 ZipCode 為空字符串,則不會被編碼到 JSON 中
}
func main() {
// 這個例子中,Age 和 Email 欄位都沒有賦值
p := Person{
FirstName: "John",
LastName: "Doe",
Addresses: []Address{
{
Street: "123 Elm St",
City: "Somewhere",
State: "CA",
},
},
}
jsonData, err := json.Marshal(p)
if err != nil {
fmt.Println("Error marshaling:", err)
return
}
fmt.Println(string(jsonData))
}
當你執行這段代碼時,Age 和 Email 欄位不會出現在 JSON 輸出中,因為它們沒有賦值,而且我們使用了 omitempty 標籤。同樣,由於 ZipCode 在 Address 結構中沒有賦值,所以它也不會出現在 JSON 輸出中。
執行結果 :
{
"fname": "John",
"lname": "Doe",
"addresses": [
{
"street": "123 Elm St",
"city": "Somewhere",
"state": "CA"
}
]
}
!!!注意事項!!!
使用omitempty時,會緊接著接在標籤逗點後面(age,omitempty),要是多一個空格(age, omitempty),
現在,Age 欄位的標籤之間有一個額外的空格。但要注意,增加這個空格可能會導致 omitempty 標籤不起作用。這是因為 Go 的 encoding/json 套件期望標籤中的選項之間沒有額外的空格。
**這樣甚至不會傳回非nil的error值,除非使用 go vet 工具來檢查,因此使用omitempty須注意標籤格式。
type book struct {
ISBN string `json:"-"` //短折線
Title string `json:"title"`
YearPublished string `json:"yearpub,omitempty"`
Author string `json:""` //沒有鍵名稱
CoAuthor string `json:`,omitempty //沒有鍵名稱
}
可以使用 MarshalIndent()函式,它的作用跟Marshall()幾乎一樣,只差結果會幫你縮排和換行 :
func MarshalIndent(v interface{}, prefix, ident string) ([]byte, error)
比起 Marshal(),MarshalIndent()函式多了兩個參數:
範例 :
package main
import (
"encoding/json"
"fmt"
)
type Person struct {
FirstName string `json:"fname"`
LastName string `json:"lname"`
Age int `json:"age,omitempty"`
Email string `json:"email,omitempty"`
Addresses []Address `json:"addresses,omitempty"`
}
type Address struct {
Street string `json:"street"`
City string `json:"city"`
State string `json:"state"`
ZipCode string `json:"zip,omitempty"`
}
func main() {
p := Person{
FirstName: "John",
LastName: "Doe",
Age: 30,
Email: "johndoe@example.com",
Addresses: []Address{
{
Street: "123 Elm St",
City: "Somewhere",
State: "CA",
ZipCode: "12345",
},
},
}
// 使用 MarshalIndent 函式,以「\t」作為縮排
jsonData, err := json.MarshalIndent(p, "", "\t")
if err != nil {
fmt.Println("Error marshaling:", err)
return
}
fmt.Println(string(jsonData))
}
以上就是簡單的如何處理JSON編碼的介紹~