Hike News
Hike News

Go初識-day23-json數據協議

Introduction

  • 目前有80%的API支持json數據協議
  • json協議為一種資料傳輸的標準,不同語言之間溝通的橋樑
  • 將golang中數據類型,如struct,map等,使用json序列化,與其他語言進行交互

json數據協議

  • 導入包:import "encoding/json"

json序列化

  • 序列化:json.Marshal(data interface{})
  • 打包成json字符串,參數可傳入各種不同的數據結構

json序列化結構體

  • 因為json為外部的package,因此欲被外部包訪問的情況下,在定義結構體時需注意字段名(屬性)須為大寫

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    package main

    import (
    "encoding/json"
    "fmt"
    "os"
    )

    type User struct{
    UserName string
    NickName string
    Age int
    Birth string
    Gender string
    Email string
    Phone string
    }

    func main() {
    Taroballz := &User{
    UserName:"Curtis",
    NickName:"Taroballz",
    Age:18,
    Birth:"2000/12/12",
    Gender:"Male",
    Email:"study@golang.com",
    Phone:"1001234567",
    }
    result, err := json.Marshal(Taroballz)
    if err != nil {
    fmt.Fprintf(os.Stderr,"json encoding Failed:%s",err)
    return
    }
    fmt.Println("json encoding result:",result)
    fmt.Println("json string is ", string(result))
    }
  • json打包的結果為[]byte類型,透過string()方法轉換為字符串

result

1
2
json encoding result: [123 34 85 115 101 114 78 97 109 101 34 58 34 67 117 114 116 105 115 34 44 34 78 105 99 107 78 97 109 101 34 58 34 84 97 114 111 98 97 108 108 122 34 44 34 65 103 101 34 58 49 56 44 34 66 105 114 116 104 34 58 34 50 48 48 48 47 49 50 47 49 50 34 44 34 71 101 110 100 101 114 34 58 34 77 97 108 101 34 44 34 69 109 97 105 108 34 58 34 115 116 117 100 121 64 103 111 108 97 110 103 46 99 111 109 34 44 34 80 104 111 110 101 34 58 34 49 48 48 49 50 51 52 53 54 55 34 125]
json string is {"UserName":"Curtis","NickName":"Taroballz","Age":18,"Birth":"2000/12/12","Gender":"Male","Email":"study@golang.com","Phone":"1001234567"}

tips

可使用json.MarshalIndent(欲格式化數據,空值替換,縮進替換)進行格式化輸出
顯示結果會較清晰,但與Marshal方法無異

1
result, err := json.MarshalIndent(Taroballz,""," ")

  • result
    1
    2
    3
    4
    5
    6
    7
    8
    9
    json string is {
    "username": "Curtis",
    "nickname": "Taroballz",
    "Age": 18,
    "Birth": "2000/12/12",
    "Gender": "Male",
    "Email": "study@golang.com",
    "Phone": "1001234567"
    }

tag

  • 透過反射(reflect)的tag將字段從大寫轉為小寫
  • 格式為json:"value"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package main

import (
"encoding/json"
"fmt"
"os"
)

type User struct{
UserName string `json:"username"`
NickName string `json:"nickname"`
Age int
Birth string
Gender string
Email string
Phone string
}

func main() {
Taroballz := &User{
UserName:"Curtis",
NickName:"Taroballz",
Age:18,
Birth:"2000/12/12",
Gender:"Male",
Email:"study@golang.com",
Phone:"1001234567",
}
result, err := json.Marshal(Taroballz)
if err != nil {
fmt.Fprintf(os.Stderr,"json encoding Failed:%s",err)
return
}
fmt.Println("json string is ", string(result))
}

result

1
json string is {"username":"Curtis","nickname":"Taroballz","Age":18,"Birth":"2000/12/12","Gender":"Male","Email":"study@golang.com","Phone":"1001234567"}
  • 透過tag可將原本需大寫的字段名在json的格式中轉為小寫

tips

  • 若是編碼後的json不想輸出至終端可使用json:"-"作為tag
  • 若是bool類型的true,false;float及int類型的數字要使用字符串作為json輸出可使用json:",string"作為tag

json序列化map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package main

import (
"encoding/json"
"fmt"
"os"
)

func testJsonMap(){
var Tom map[string]interface{} = map[string]interface{}{"Age":18}
Tom["Name"] = "Tom"
Tom["gender"] = "Male"
data,err := json.Marshal(Tom)
if err != nil {
fmt.Fprintln(os.Stderr,"json encoding failed:",err)
return
}
fmt.Println("json encoding result:",string(data))
}

func main() {
testJsonMap()
}

result

1
json encoding result: {"Age":18,"Name":"Tom","gender":"Male"}

json序列化切片(slice)

  • 使用json序列化一個map類型的切片
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package main

import (
"encoding/json"
"fmt"
"os"
)

func testJsonSlice(){
var userinfo []map[string]interface{}
userinfo = make([]map[string]interface{},0)
userinfo = append(userinfo,map[string]interface{}{"Name":"Amy"})
userinfo[0]["Age"] = 18
fmt.Println(cap(userinfo))

userinfo = append(userinfo,map[string]interface{}{"Name":"Tom"})
data, err := json.Marshal(userinfo)
if err != nil {
fmt.Fprintln(os.Stderr,"json encoding failed:",err)
return
}
fmt.Println("json encoding result:",string(data))
}

func main() {
testJsonSlice()
}

result

1
2
1
json encoding result: [{"Age":18,"Name":"Amy"},{"Name":"Tom"}]
  • 可以看到slice的數據化類型為[ ]陣列形式

json反序列化

在別人系統或是不同語言中拿到json的數據類型並反序列化成golang中的數據類型,並進行其他操作

  • 反序列化:json.UnMarshal(data []byte, v interface{})
  • 將json字符串反序列化成golang的數據類型
  • data為要反序列化的json字符串
  • v為不同數據類型的實例

反序列化結構體

  • 在反序列化之前要保證反序列化的類型字段跟欲接收的容器是一樣的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package main

import (
"encoding/json"
"fmt"
)

type User struct{
UserName string `json:"username"`
NickName string `json:"nickname"`
Age int
Birth string
Gender string
Email string
Phone string
}

func testStruct()(Error error, user string){
Taroballz := &User{
UserName:"Curtis",
NickName:"Taroballz",
Age:18,
Birth:"2000/12/12",
Gender:"Male",
Email:"study@golang.com",
Phone:"1001234567",
}
result, err := json.Marshal(Taroballz)
if err != nil {
Error = fmt.Errorf("json encoding Failed:%s",err)
return
}
user = string(result)
return
}

func testUnMarshallStruct(data string)(ret User, err error){
var container User
err = json.Unmarshal([]byte(data),&container)
if err != nil {
err = fmt.Errorf("UnMarshal Failed")
return
}
ret = container
return
}

func main() {
err,data := testStruct()
if err != nil {
fmt.Println(err)
return
}
result ,err := testUnMarshallStruct(data)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("result is",result)
}

result

1
Result is : {Curtis Taroballz 18 2000/12/12 Male study@golang.com 1001234567}

tips

  • UnMarshall只會解析所需要的字段置入結構體中

反序列化map

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package main

import (
"encoding/json"
"fmt"
)

func testJsonMap()(ret string, error error){
var Tom map[string]interface{} = map[string]interface{}{"Age":18}
Tom["Name"] = "Tom"
Tom["gender"] = "Male"
data,err := json.Marshal(Tom)
if err != nil {
error = fmt.Errorf("json encoding failed")
return
}
ret = string(data)
return
}

func testUnMarshalMap(data string)(ret map[string]interface{},error error){
var container map[string]interface{}
err := json.Unmarshal([]byte(data),&container) //即使map為引用類型 仍需對map取址
if err != nil{
error = fmt.Errorf("Unmarshal failed:",err)
return
}
ret = container
return
}

func main() {
data,err := testJsonMap()
if err != nil {
fmt.Println(err)
return
}
result ,err := testUnMarshalMap(data)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("result is",result)
}
  • 可以看到在創建接收map類型的變量時,並沒有使用make()初始化map,亦就是說map在json.unmarshal()函數中才分配,只需將對應類型的變量傳入就好
  • 往map中插入數據時,才會因為map是引用類型無須取址,但是改變map本身所儲存的資料則需取址

result

1
result is map[Name:Tom gender:Male Age:18]

tips

  • 在json數據的量比較大的情況下,標準庫encoding/json會不敷使用
  • 這時可以使用ffjson進行序列化及反序列化
    • go get github.com/pquerna/ffjson/ffjson
    • 其使用方法與encoding/json使用方法差不多
    • 其速度為encoding/json的3到4倍