重學(xué)Go語言之JSON操作詳解
當(dāng)你想在項(xiàng)目中保存配置時(shí),你會(huì)選擇什么數(shù)據(jù)格式,你可能會(huì)說用yaml
或是json
,當(dāng)你作為后端要把數(shù)據(jù)返回給前端時(shí),你會(huì)選擇什么數(shù)據(jù)格式,這次我想,一定是json
了吧。
關(guān)于在Go語言如何操作JSON
,在這篇文章我們來探究一下!
JSON
什么是JSON
?JSON
的全稱是Javascript Object Notation
,是一種數(shù)據(jù)結(jié)構(gòu)化交互的標(biāo)準(zhǔn)協(xié)議,非常容易閱讀與編寫,可以作為配置文件保存應(yīng)用的相關(guān)配置信息,也可以作為前后端接口數(shù)據(jù)的格式,比如后端返回給前端這樣的JSON數(shù)據(jù):
{ "code":404, "msg":"Not Found" }
JSON的數(shù)據(jù)類型
JSON只支持六種數(shù)據(jù)類型,分別是:
- 數(shù)字(
number
):有十進(jìn)制和科學(xué)記數(shù)學(xué)兩種表示方式。 - 字符串(
string
):使用雙引號(hào)表示的Unicode
字符序列。 - 布爾(
bool)
:true
或者false
。 - 對(duì)象(
object
):使用花括號(hào)({}
)括起來的一個(gè)或多個(gè)鍵值對(duì)(key/value
),用逗號(hào)(,)
隔開,最后一個(gè)鍵值對(duì)后面不能有逗號(hào),鍵(key
)必是雙引號(hào)(""
)引起來的字符串,而值則可以是任意類型(如:布爾、數(shù)字、對(duì)象、數(shù)組、字符串)。 - 數(shù)組(
array
):使用中括號(hào)([]
)括起來的值的集合,這些值可是任意類型(布爾、數(shù)字、對(duì)象、數(shù)組、字符串)。 - null:空值。
JSON類型與Go數(shù)據(jù)類型的對(duì)應(yīng)關(guān)系:
- Go的
boolean
對(duì)應(yīng)JSON的bool類型 - Go的整型與浮點(diǎn)型對(duì)應(yīng)JSON的number類
- Go的map和struct對(duì)應(yīng)JSON的object,map的key必須是字符串
- Go的切片與數(shù)組對(duì)應(yīng)JSON的array
- Go的
chan
與函數(shù)等復(fù)雜的類型不能作為JSON的值
JSON的基本操作
Go語言在encoding/json
包中提供了JSON數(shù)據(jù)序列化與反序列化的操作,最簡(jiǎn)單直接就是調(diào)用Marshal
把一個(gè)Go的數(shù)據(jù)序列化為一個(gè)JSON數(shù)據(jù)和調(diào)用Unmarshal
對(duì)JSON數(shù)據(jù)反序列化。
Marshal
json
包的Marshal()
函數(shù)用于將數(shù)據(jù)編碼為一個(gè)JSON
文本,該函數(shù)的簽名如下所示:
func Marshal(v interface{}) ([]byte, error)
Marshal()
函數(shù)可以接受任意值,并返回JSON
化的字節(jié)數(shù)組與一個(gè)error
類型:
package main import ( "encoding/json" "fmt" ) func main() { m := map[string]int{"Go入門": 1, "Go Ation": 2, "Go從入門到精通": 3, "Go高級(jí)編程": 4} data, err := json.Marshal(m) if err != nil { panic(err) } //轉(zhuǎn)換為string并輸出 fmt.Println(string(data)) }
上面示例程序的運(yùn)行結(jié)果:
{"Go Ation":2,"Go從入門到精通":3,"Go入門":1,"Go高級(jí)編程":4}
MarshalIndent
上面調(diào)用Marshal
函數(shù)序列化的數(shù)據(jù)輸出了一行,如果數(shù)據(jù)太長(zhǎng),則不利于我們閱讀,要想輸出的JSON數(shù)據(jù)格式更友好一點(diǎn),可以用MarshalIndent
函數(shù),該函數(shù)簽名如下:
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)
相比于Marshal
函數(shù),MarshalIndent
函數(shù)可以接收一個(gè)prefix
和indent
,用于設(shè)置輸出JSON每一行的前綴和首行縮進(jìn),下面是一個(gè)使用該函數(shù)的例子:
package main import ( "encoding/json" "fmt" ) func main() { m := map[string]int{"Go入門": 1, "Go Ation": 2, "Go從入門到精通": 3, "Go高級(jí)編程": 4} //設(shè)置prefix為空,indent為tab data, err := json.MarshalIndent(m, "", "\t") if err != nil { panic(err) } fmt.Println(string(data)) }
設(shè)置了indent
參數(shù)后的運(yùn)行結(jié)果:
{ "Go Ation": 2, "Go從入門到精通": 3, "Go入門": 1, "Go高級(jí)編程": 4 }
json.Unmarshal
json
包的Unmarshal()
函數(shù)作用與Marshal
剛好相反,用于將一個(gè)JSON
數(shù)據(jù)格式反序列化到一個(gè)我們定義好的Go類型變量中,該函數(shù)的簽名如下所示:
func Unmarshal(data []byte, v interface{}) error
下面是一個(gè)反序列化的示例:
package main import ( "encoding/json" "fmt" ) type Book struct { BookName string `json:"name"` Number int `json:"num"` } func main() { jsonStr := `[{"name":"Go入門","num": 1}, {"name":"Go Ation","num": 2}, {"name":"Go從入門到精通","num": 3}, {"name":"Go高級(jí)編程","num": 4}]` var books []Book err := json.Unmarshal([]byte(jsonStr), &books) if err != nil { panic(err) } fmt.Println(books) }
上面的例子中,我們將一個(gè)JSON反序化到一個(gè)數(shù)組當(dāng)中,而該數(shù)組的元素是一個(gè)自定義的struct類型,在struct中可以通過json tag
來定義json的字段名稱。
自定義序列化
如果是自定義的數(shù)據(jù)類型,我們可以實(shí)現(xiàn)Marshaler
接口和Unmarshaler
接口的方法,這兩個(gè)接口的定義如下:
type Unmarshaler interface { UnmarshalJSON([]byte) error } type Marshaler interface { MarshalJSON() ([]byte, error) }
當(dāng)調(diào)用Marshal
函數(shù)序列化時(shí),會(huì)調(diào)用MarshalJSON()
函數(shù),我們可以在該函數(shù)中自定義序列化的邏輯:
package main import ( "encoding/json" "fmt" "strings" ) type Reason uint8 const ( Unknown = iota Spring Summer Autumn Winter ) //自定義反序列邏輯 func (r *Reason) UnmarshalJSON(b []byte) error { var s string if err := json.Unmarshal(b, &s); err != nil { return err } switch strings.ToLower(s) { default: *r = Unknown case "spring": *r = Spring case "summer": *r = Summer case "autumn": *r = Autumn case "winter": *r = Winter } return nil } //自定義序列邏輯 func (r Reason) MarshalJSON() ([]byte, error) { var s string switch r { default: s = "unknonw" case Spring: s = "spring" case Summer: s = "summer" case Autumn: s = "autumn" case Winter: s = "winter" } return json.Marshal(s) } func main() { r := []Reason{1, 2, 3, 4} data, err := json.Marshal(r) if err != nil { panic(err) } fmt.Println(string(data)) }
上面的代碼中,Reason
也實(shí)現(xiàn)了Unmarshaler
接口,因此在調(diào)用Unmarshal
函數(shù)反序列化時(shí)會(huì)調(diào)用UnmarshalJSON
,在該方法中我們可以自定義反序列化的邏輯:
func main() { jsonStr := `["winter","spring","test"]` var r []Reason err := json.Unmarshal([]byte(jsonStr), &r) if err != nil { panic(err) } fmt.Println(r) }
Encoder與Decoder
調(diào)用Marshal
只會(huì)返回一個(gè)字節(jié)數(shù)組,Unmarshal
函數(shù)只能傳入一個(gè)字節(jié)數(shù)組,如果我們想將序列化的數(shù)據(jù)寫入到不同的地方(比如保存到一個(gè)文件中),或者想從不同地方讀取JSON數(shù)據(jù)(比如從一個(gè)文件讀取),那么用Encoder
和Decoder
會(huì)更方便。
json.Encoder
NewEncoder
函數(shù)可以創(chuàng)建一個(gè)Encoder
結(jié)構(gòu)體,NewEncoder
函數(shù)接收一個(gè)實(shí)現(xiàn)了io.Writer
接口的參數(shù),在序列化后會(huì)調(diào)用io.Writer
寫入數(shù)據(jù):
package main import ( "encoding/json" "os" ) func main() { m := map[string]int{"Go入門": 1, "Go Ation": 2, "Go從入門到精通": 3, "Go高級(jí)編程": 4} file, err := os.Create("config.json") if err != nil { panic(err) } defer file.Close() encoder := json.NewEncoder(file) encoder.SetIndent("", "\t") err = encoder.Encode(m) if err != nil { panic(err) } }
上面程序運(yùn)行后,會(huì)把JSON數(shù)據(jù)保存到當(dāng)前目錄的config.json
文件當(dāng)中。
json.Decoder
NewDecoder
函數(shù)可以創(chuàng)建一個(gè)Decoder
結(jié)構(gòu)體,NewDecoder
函數(shù)接收一個(gè)實(shí)現(xiàn)了io.Reader
接口的參數(shù),也就是說Decoder
從io.Reader
中讀取數(shù)據(jù):
package main import ( "encoding/json" "fmt" "os" ) func main() { file, err := os.Open("config.json") if err != nil { panic(err) } defer file.Close() decoder := json.NewDecoder(file) var m map[string]int err = decoder.Decode(&m) if err != nil { panic(err) } fmt.Println(m) }
小結(jié)
JSON
是一種非常通用的數(shù)據(jù)格式,經(jīng)常被用于前后端api
接口的數(shù)據(jù)通信,而Go語言在標(biāo)準(zhǔn)庫encoding/json
包中為JSON
數(shù)據(jù)的編碼與反編碼提供非常好的支持。
好了,相信看完這篇文章,你應(yīng)該掌握了以下幾點(diǎn)了吧:
- 什么是JSON
- 如何序列化與反序列化JSON數(shù)據(jù)
- 如何自定義序列化過程
- json.Encoder與json.Decoder的使用
到此這篇關(guān)于重學(xué)Go語言之JSON操作詳解的文章就介紹到這了,更多相關(guān)Go JSON內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go語言輕松實(shí)現(xiàn)郵件發(fā)送通知功能的完全指南
在現(xiàn)代 Web 應(yīng)用中,郵件通知是一個(gè)不可或缺的功能,本文將深入解析一個(gè)基于 Go 語言 smtp 協(xié)議和 email 庫的郵件發(fā)送工具,需要的可以了解下2025-04-04Golang中調(diào)用deepseekr1的教程詳解
這篇文章主要為大家詳細(xì)介紹了Golang中調(diào)用deepseekr1的相關(guān)教程,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,感興趣的小伙伴可以了解下2025-02-02