JSON(JavaScript Object Notation)是一種輕量級的資料交換語言,以純文字為基礎去儲存資料,有相容性高、易於理解且許多程式語言都支援等優點。
而在 Golang 之中是如何處理 JSON 的呢?
Golang 官方的 package 中已有支援 JSON,可很容易的對 JSON 數據做編解碼
數據結構
types |
JSON |
Golang |
字串 |
string |
string |
整數 |
number |
int |
浮點數 |
number |
flaot64 |
陣列 |
array |
slice |
物件 |
object |
struct |
布林 |
bool |
bool |
空值 |
null |
nil |
JSON 編碼
JSON 套件裡面透過 Marshal 函數來將數據處理成 JSON 字串
1
|
func Marshal(v interface{}) ([]byte, error)
|
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
|
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int
Name string
Money float64
Skills []string
Relationship map[string]string
Identification Identification
}
type Identification struct {
Phone bool
Email bool
}
func main() {
user := User {
ID: 1,
Name: "Tony",
Skills: []string{"program", "rich", "play"},
Relationship: map[string]string {
"Dad": "Hulk",
"Mon": "Natasha",
},
Identification: Identification {
Phone: true,
Email: false,
},
}
b, err := json.Marshal(user)
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(b))
}
|
輸出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
{
"ID":1,
"Name":"Tony",
"Money":0,
"Skills":
[
"program",
"rich","play"
],
"Relationship":
{
"Dad":"Hulk",
"Mon":"Natasha"
},
"Identification":
{
"Phone":true,
"Email":false
}
}
|
從上面的例子看到 key 值都是大寫,因只有大寫的才會被輸出,但若是需要小寫或其他的名稱怎麼辦呢? Golang 提供了 struct tag
來實現
1
2
3
4
5
6
|
...
type Identification struct {
Phone bool `json:"phone"`
Email bool `json:"email"`
}
...
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
{
"ID":1,
"Name":"Tony",
"Money":0,
"Skills":
[
"program","rich","play"
],
"Relationship":
{
"Dad":"Hulk",
"Mon":"Natasha"
},
"Identification":
{
"phone":true,
"email":false
}
}
|
struct tag 還可以有以下功能(取自官方文件)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// Field appears in JSON as key "myName".
Field int `json:"myName"`
// Field appears in JSON as key "myName" and
// the field is omitted from the object if its value is empty,
// as defined above.
Field int `json:"myName,omitempty"`
// Field appears in JSON as key "Field" (the default), but
// the field is skipped if empty.
// Note the leading comma.
Field int `json:",omitempty"`
// Field is ignored by this package.
Field int `json:"-"`
// Field appears in JSON as key "-".
Field int `json:"-,"`
|
又或者需要將輸出全部改為 String,Golang 也有提供此功能
1
|
Int64String int64 `json:",string"`
|
JSON 解碼
相較 JSON 編碼來說,Golang 對於未知的 JSON 實在是一大挑戰,一開始我們先從已知的結構來看
透過 Unmarshal 將 JSON 字串處理成對應的結構
1
|
func Unmarshal(data []byte, v interface{}) error
|
直接來看例子吧
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
|
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int
Name string
Money float64
Skills []string
Relationship map[string]string
Identification Identification
}
type Identification struct {
Phone bool `json:"phone"`
Email bool `json:"email"`
}
func main() {
var jsonBlob = []byte(`{"ID":1,"Name":"Tony","Money":0,"Skills":["program","rich","play"],"Relationship":{"Dad":"Hulk","Mon":"Natasha"},"Identification":{"phone":true,"email":false}}`)
var user User
err := json.Unmarshal(jsonBlob, &user)
if err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%+v", user)
}
|
解碼與編碼類似,但接受大小寫不同,不過如果欄位是私有的則不會配對
struct tag
如同在編碼的時候,解碼也有對應的 tag,就讓我們來看看吧
- string tag 表示只有字串會被解析,當傳入的值不是字串時會報錯
1
2
3
4
5
6
7
8
9
10
|
...
type User struct {
ID int
Name string
Money float64 `json:",string"`
Skills []string
Relationship map[string]string
Identification Identification
}
...
|
正確:
"Money":"100.5"
錯誤:
"Money":100.5
error: json: invalid use of ,string struct tag, trying to unmarshal unquoted value into float64
動態解析 JSON
以上介紹的都是理想的狀況,但實際開發時會常常遇到未知的 JSON 格式,或是動態變化的結構,這時候就可以利用以下方法
interface & assertion
先利用 interface 來儲存任意的數據結構,再利用 assertion 的方式解析數據
範例 a:interface 儲存輸入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
package main
import (
"encoding/json"
"fmt"
)
func main() {
var user map[string]interface{}
var jsonString string = `{"ID":1, "Name":"Tony"}`
if err := json.Unmarshal([]byte(jsonString), &user); err != nil {
fmt.Println("ERROR:",err)
}
fmt.Printf("%#v\n", user)
}
|
範例 b:用 assertion 來解析數據
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
|
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int
Name string
Money float64 `json:",string"`
Skills []string
Relationship map[string]string
Identification Identification
Career string
Responsibility interface {}
}
type Identification struct {
Phone bool `json:"phone"`
Email bool `json:"email"`
}
func main() {
var jsonBlob = []byte(`[
{
"ID":1,
"Name":"Tony",
"Career":"Engineer",
"Responsibility":{
"skill":"PHP&Golang&Network",
"description":"coding"
}
},
{
"ID":2,
"Name":"Jim",
"Career":"Manager",
"Responsibility":{
"experienced":true
}
}
]`)
var users []User
if err := json.Unmarshal(jsonBlob, &users); err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%#v\n", users)
fmt.Println(users[0].Responsibility.(map[string]interface{})["description"].(string))
fmt.Println(users[1].Responsibility.(map[string]interface{})["experienced"].(bool))
}
|
雖然可利用上述方式,但是 code 實在不是很美觀,尤其是在讀出資料的時候
利用 *json.RawMessage 延遲解析
上述結構內的 Responsibility 實際上是在解析 Career 才會知道它的資料結構,這時候就可以使用 json.RawMessage 來延遲解析,讓數據以 byte 的方式繼續存在,等待下一次的解析
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
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
package main
import (
"encoding/json"
"fmt"
)
type User struct {
ID int
Name string
Money float64 `json:",string"`
Skills []string
Relationship map[string]string
Identification Identification
Career string
Responsibility json.RawMessage
}
type Identification struct {
Phone bool `json:"phone"`
Email bool `json:"email"`
}
type Engineer struct {
Skill string `json:"skill"`
Description string `json:"description"`
}
type Manager struct {
Experienced bool `json:"experienced"`
}
func main() {
var jsonBlob = []byte(`[
{
"ID":1,
"Name":"Tony",
"Career":"Engineer",
"Responsibility":{
"skill":"PHP&Golang&Network",
"description":"coding"
}
},
{
"ID":2,
"Name":"Jim",
"Career":"Manager",
"Responsibility":{
"experienced":true
}
}
]`)
var users []User
if err := json.Unmarshal(jsonBlob, &users); err != nil {
fmt.Println("error:", err)
}
for _, user := range users {
switch user.Career {
case "Engineer":
var responsibility Engineer
if err := json.Unmarshal(user.Responsibility, &responsibility); err != nil {
fmt.Println("error:", err)
}
fmt.Println(responsibility.Description)
case "Manager":
var responsibility Manager
if err := json.Unmarshal(user.Responsibility, &responsibility); err != nil {
fmt.Println("error:", err)
}
fmt.Println(responsibility.Experienced)
default:
fmt.Println("warning:", "don't exist")
}
}
}
|