一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Golang - go json轉換實踐中遇到的坑

go json轉換實踐中遇到的坑

2020-05-22 09:30simpleapples Golang

在使用 go 語言開發過程中,經常需要使用到 json 包來進行 json 和 struct 的互相轉換,這篇文章主要介紹了go json轉換實踐中遇到的坑,非常具有實用價值,需要的朋友可以參考下

在使用 go 語言開發過程中,經常需要使用到 json 包來進行 json 和 struct 的互相轉換,在使用過程中,遇到了一些需要額外注意的地方,記錄如下。

整數變浮點數問題

假設有一個 Person 結構,其中包含 Age int64 和 Weight float64 兩個字段,現在通過 json 包將 Person 結構轉為 map[string]interface{},代碼如下。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
type Person struct {
 Name string
 Age int64
 Weight float64
}
 
func main() {
 person := Person{
  Name: "Wang Wu",
  Age: 30,
  Weight: 150.07,
 }
 
 jsonBytes, _ := json.Marshal(person)
 fmt.Println(string(jsonBytes))
 
 var personFromJSON interface{}
 json.Unmarshal(jsonBytes, &personFromJSON)
 
 r := personFromJSON.(map[string]interface{})
}

代碼執行到這里看上去一切正常,但是打印一下 map[string]interface{} 就會發現不太對了。

?
1
2
fmt.Println(reflect.TypeOf(r["Age"]).Name()) // float64
fmt.Println(reflect.TypeOf(r["Weight"]).Name()) // float64

轉換成 map[string]interface{} 之后,原先的 uint64 和 float64 類型都被轉換成了 float64 類型,這顯然是不符合我們的預期的。

go json轉換實踐中遇到的坑

查看 json 的規范可以看到,在 json 中是沒有整型和浮點型之分的,所以現在可以理解 json 包中的 Unmarshal 方法轉出的數字類型為什么都是 float64 了,因為根據 json 規范,數字都是同一種類型,那么對應到 go 的類型中最接近的就是 float64 了。

json 包還針對這個問題提供了更好的解決方案,不過需要使用 json.Decoder 來代替 json.Unmarshal 方法,將 json.Unmarhsal 替換如下。

?
1
2
3
4
5
6
7
var personFromJSON interface{}
 
decoder := json.NewDecoder(bytes.NewReader(jsonBytes))
decoder.UseNumber()
decoder.Decode(&personFromJSON)
 
r := personFromJSON.(map[string]interface{})

這種方法首先創建了一個 jsonDecoder,然后調用了 UseNumber 方法,從文檔中可以知道,使用 UseNumber 方法后,json 包會將數字轉換成一個內置的 Number 類型(而不是 float64),這個 Number 類型提供了轉換為 int64、float64 等多個方法。

go json轉換實踐中遇到的坑

時間格式

對于 json 格式,是沒有時間類型的,日期和時間以 json 格式存儲時,需要轉換為字符串類型。這就帶來了一個問題,日期時間的字符串表示有多種多樣,go 的 json 包支持的是哪一種呢?

使用下面的代碼來輸出 json.Marshal 方法將 Time 類型轉換為字符串后的格式。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
type Person struct {
 Name string
 Birth time.Time
}
 
func main() {
 person := Person{
  Name: "Wang Wu",
  Birth: time.Now(),
 }
 
 jsonBytes, _ := json.Marshal(person)
 fmt.Println(string(jsonBytes)) // {"Name":"Wang Wu","Birth":"2018-12-20T16:22:02.00287617+08:00"}
}

根據輸出可以判斷,go 的 json 包使用的是 RFC3339 標準中定義的格式。接下來測試一下 json.Unmarshal 方法所支持的日期時間格式。

?
1
2
3
4
5
6
7
dateStr := "2018-10-12"
 
var person Person
jsonStr := fmt.Sprintf("{\"name\":\"Wang Wu\", \"Birth\": \"%s\"}", dateStr)
json.Unmarshal([]byte(jsonStr), &person)
 
fmt.Println(person.Birth) // 0001-01-01 00:00:00 +0000 UTC

對于形如 2018-10-12 的字符串,json 包并沒有成功將其解析,接下來我們把 time 包中支持的所有格式都試一下。

go json轉換實踐中遇到的坑

經過試驗,發現 json.Unmarshal 方法只支持 RFC3339 和 RFC3339Nano 兩種格式的轉換。還有一個需要注意的地方,使用 time.Now() 生成的時間是帶有一個 Monotonic Time 的,經過 json.Marshal 轉換時候,由于 RFC3339 規范里沒有存放 Monotonic Time 的位置,會丟掉這一部分。

對于字段為空的處理

json 包對于空值的處理是一個非常容易出錯的地方,看下面代碼。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
type Person struct {
 Name  string
 Age  int64
 Birth time.Time
 Children []Person
}
 
func main() {
 person := Person{}
 
 jsonBytes, _ := json.Marshal(person)
 fmt.Println(string(jsonBytes)) // {"Name":"","Age":0,"Birth":"0001-01-01T00:00:00Z","Children":null}
}

當 struct 中的字段沒有值時,使用 json.Marshal 方法并不會自動忽略這些字段,而是根據字段的類型輸出了他們的默認空值,這往往和我們的預期不一致,json 包提供了對字段的控制手段,我們可以為字段增加 omitempty tag,這個 tag 會在字段值為零值(int 和 float 類型零值是 0,string 類型零值是 "",對象類型零值是 nil)時,忽略該字段。

?
1
2
3
4
5
6
7
8
9
10
11
12
type PersonAllowEmpty struct {
 Name  string    `json:",omitempty"`
 Age  int64    `json:",omitempty"`
 Birth time.Time   `json:",omitempty"`
 Children []PersonAllowEmpty `json:",omitempty"`
}
 
func main() {
 person := PersonAllowEmpty{}
 jsonBytes, _ := json.Marshal(person)
 fmt.Println(string(jsonBytes)) // {"Birth":"0001-01-01T00:00:00Z"}
}

可以看到,這次輸出的 json 中只有 Birth 字段了,string、int、對象類型的字段,都因為沒有賦值,默認是零值,所以被忽略,對于日期時間類型,由于不可以設置為零值,也就是 0000-00-00 00:00:00,不會被忽略。

需要注意這樣的情況:如果一個人的年齡是 0 (對于剛出生的嬰兒,這個值是合理的),剛好是 int 字段的零值,在添加 omitempty tag 的情況下,年齡字段會被忽略。

如果想要某一個字段在任何情況下都被 json 包忽略,需要使用如下的寫法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type Person struct {
 Name  string `json:"-"`
 Age  int64 `json:"-"`
 Birth time.Time `json:"-"`
 Children []string `json:"-"`
}
 
func main() {
 birth, _ := time.Parse(time.RFC3339, "1988-12-02T15:04:27+08:00")
 person := Person{
  Name: "Wang Wu",
  Age: 30,
  Birth: birth,
  Children: []string{},
 }
 
 jsonBytes, _ := json.Marshal(person)
 fmt.Println(string(jsonBytes)) // {}
}

可以看到,使用 json:"-" 標簽的字段都被忽略了。

補充:golang string轉json的一些坑

先看一段代碼,起作用是把字符串轉換為結構體對應的json

?
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
type people struct {
 
 name string `json:"name"`
 
 age int `json:"age"`
 
 id int `json:"id"`
 
}
 
 
 
type student struct {
 
 people
 
 id int `json:"sid"`
 
}
 
 
 
func main() {
 
 msg := "{\"name\":\"zhangsan\", \"age\":18, \"id\":122463, \"sid\":122464}"
 
 var someOne student
 
 if err := json.Unmarshal([]byte(msg), &someOne); err == nil {
 
  fmt.Println(someOne)
 
  fmt.Println(someOne.people)
 
 } else {
 
  fmt.Println(err)
 
 }
 
}

仔細看看,有沒有錯?我只能說,這樣是輸出不出來答案的,賦值錯誤,看下面的運行結果:

go json轉換實踐中遇到的坑

傷腦筋啊,我仔細看了半天,發現在定義的people和student兩個結構體下邊有綠色的波浪線(我用的vscode),像下邊這樣:

go json轉換實踐中遇到的坑

鼠標放上去顯示的是:

go json轉換實踐中遇到的坑

大家都知道,golang中變量聲明成大寫和小寫能引用的范圍是不一樣的,那我就想了,大小寫問題???一臉懵逼把變量名首字母改成了大寫,然后...就行了,代碼變成了下邊這樣:

?
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
type people struct {
 
 Name string `json:"name"`
 
 Age int `json:"age"`
 
 ID int `json:"id"`
 
}
 
 
 
type student struct {
 
 people
 
 ID int `json:"sid"`
 
}
 
 
 
func main() {
 
 msg := "{\"name\":\"zhangsan\", \"age\":18, \"id\":122463, \"sid\":122464}"
 
 var someOne student
 
 if err := json.Unmarshal([]byte(msg), &someOne); err == nil {
 
  fmt.Println(someOne)
 
  fmt.Println(someOne.people)
 
 } else {
 
  fmt.Println(err)
 
 }
 
}

輸出的結果這樣:

go json轉換實踐中遇到的坑

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://www.jianshu.com/p/2b4a3cda0f6f

 

延伸 · 閱讀

精彩推薦
  • Golanggo日志系統logrus顯示文件和行號的操作

    go日志系統logrus顯示文件和行號的操作

    這篇文章主要介紹了go日志系統logrus顯示文件和行號的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    SmallQinYan12302021-02-02
  • Golanggolang json.Marshal 特殊html字符被轉義的解決方法

    golang json.Marshal 特殊html字符被轉義的解決方法

    今天小編就為大家分享一篇golang json.Marshal 特殊html字符被轉義的解決方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧 ...

    李浩的life12792020-05-27
  • Golanggolang 通過ssh代理連接mysql的操作

    golang 通過ssh代理連接mysql的操作

    這篇文章主要介紹了golang 通過ssh代理連接mysql的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    a165861639710342021-03-08
  • Golanggolang如何使用struct的tag屬性的詳細介紹

    golang如何使用struct的tag屬性的詳細介紹

    這篇文章主要介紹了golang如何使用struct的tag屬性的詳細介紹,從例子說起,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看...

    Go語言中文網11352020-05-21
  • Golanggo語言制作端口掃描器

    go語言制作端口掃描器

    本文給大家分享的是使用go語言編寫的TCP端口掃描器,可以選擇IP范圍,掃描的端口,以及多線程,有需要的小伙伴可以參考下。 ...

    腳本之家3642020-04-25
  • Golanggolang的httpserver優雅重啟方法詳解

    golang的httpserver優雅重啟方法詳解

    這篇文章主要給大家介紹了關于golang的httpserver優雅重啟的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,...

    helight2992020-05-14
  • GolangGolang通脈之數據類型詳情

    Golang通脈之數據類型詳情

    這篇文章主要介紹了Golang通脈之數據類型,在編程語言中標識符就是定義的具有某種意義的詞,比如變量名、常量名、函數名等等,Go語言中標識符允許由...

    4272021-11-24
  • GolangGolang中Bit數組的實現方式

    Golang中Bit數組的實現方式

    這篇文章主要介紹了Golang中Bit數組的實現方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧...

    天易獨尊11682021-06-09
主站蜘蛛池模板: 日本xxwwwxxxx | 天天av天天翘天天综合网 | 亚洲ⅴa偷拍在线影院 | 99在线精品视频 | 女同69式互添在线观看免费 | 国产99页| 香蕉精品国产高清自在自线 | 精品午夜寂寞影院在线观看 | 手机在线伦理片 | 国产日韩欧美在线一区二区三区 | 久久久免费观成人影院 | 国产自在自线午夜精品之la | 粉嫩高中生第一次不戴套 | 黑人双渗透 | 国产精品夜夜爽张柏芝 | 99精品国产自在现线观看 | 国产精品自在欧美一区 | 日韩毛片大全免费高清 | 亚洲妇熟xxxxx妇色黄 | 国产一区二区三区高清视频 | 视频网站入口在线看 | 甜宠巨肉h文1v1校园 | 国产精品久久久久久久久 | 久青草国产在视频在线观看 | 成年女人毛片免费观看97 | 国产亚洲精品福利在线 | 娇喘高潮教室h | 亚洲欧美久久婷婷爱综合一区天堂 | 激情影院免费 | 无人区免费一二三四乱码 | 含羞草传媒每天免费一次破解 | 亚洲精品免费在线观看 | 日本性生活免费看 | 色呦呦网| 黄色a站| 亚洲日本在线观看网址 | 草嫩社区 | 双性肉文h | 无码国产成人777爽死 | 四虎精品成人免费观看 | 国产亚洲欧美在线中文bt天堂网 |