重學(xué)Go語言之JSON操作詳解
當(dāng)你想在項(xiàng)目中保存配置時,你會選擇什么數(shù)據(jù)格式,你可能會說用yaml或是json,當(dāng)你作為后端要把數(shù)據(jù)返回給前端時,你會選擇什么數(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):使用雙引號表示的Unicode字符序列。 - 布爾(
bool):true或者false。 - 對象(
object):使用花括號({})括起來的一個或多個鍵值對(key/value),用逗號(,)隔開,最后一個鍵值對后面不能有逗號,鍵(key)必是雙引號("")引起來的字符串,而值則可以是任意類型(如:布爾、數(shù)字、對象、數(shù)組、字符串)。 - 數(shù)組(
array):使用中括號([])括起來的值的集合,這些值可是任意類型(布爾、數(shù)字、對象、數(shù)組、字符串)。 - null:空值。
JSON類型與Go數(shù)據(jù)類型的對應(yīng)關(guān)系:
- Go的
boolean對應(yīng)JSON的bool類型 - Go的整型與浮點(diǎn)型對應(yīng)JSON的number類
- Go的map和struct對應(yīng)JSON的object,map的key必須是字符串
- Go的切片與數(shù)組對應(yīng)JSON的array
- Go的
chan與函數(shù)等復(fù)雜的類型不能作為JSON的值
JSON的基本操作
Go語言在encoding/json包中提供了JSON數(shù)據(jù)序列化與反序列化的操作,最簡單直接就是調(diào)用Marshal把一個Go的數(shù)據(jù)序列化為一個JSON數(shù)據(jù)和調(diào)用Unmarshal對JSON數(shù)據(jù)反序列化。
Marshal
json包的Marshal()函數(shù)用于將數(shù)據(jù)編碼為一個JSON文本,該函數(shù)的簽名如下所示:
func Marshal(v interface{}) ([]byte, error) Marshal()函數(shù)可以接受任意值,并返回JSON化的字節(jié)數(shù)組與一個error類型:
package main
import (
"encoding/json"
"fmt"
)
func main() {
m := map[string]int{"Go入門": 1, "Go Ation": 2, "Go從入門到精通": 3, "Go高級編程": 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高級編程":4}
MarshalIndent
上面調(diào)用Marshal函數(shù)序列化的數(shù)據(jù)輸出了一行,如果數(shù)據(jù)太長,則不利于我們閱讀,要想輸出的JSON數(shù)據(jù)格式更友好一點(diǎn),可以用MarshalIndent函數(shù),該函數(shù)簽名如下:
func MarshalIndent(v interface{}, prefix, indent string) ([]byte, error)相比于Marshal函數(shù),MarshalIndent函數(shù)可以接收一個prefix和indent,用于設(shè)置輸出JSON每一行的前綴和首行縮進(jìn),下面是一個使用該函數(shù)的例子:
package main
import (
"encoding/json"
"fmt"
)
func main() {
m := map[string]int{"Go入門": 1, "Go Ation": 2, "Go從入門到精通": 3, "Go高級編程": 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高級編程": 4
}json.Unmarshal
json包的Unmarshal()函數(shù)作用與Marshal剛好相反,用于將一個JSON數(shù)據(jù)格式反序列化到一個我們定義好的Go類型變量中,該函數(shù)的簽名如下所示:
func Unmarshal(data []byte, v interface{}) error 下面是一個反序列化的示例:
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高級編程","num": 4}]`
var books []Book
err := json.Unmarshal([]byte(jsonStr), &books)
if err != nil {
panic(err)
}
fmt.Println(books)
}上面的例子中,我們將一個JSON反序化到一個數(shù)組當(dāng)中,而該數(shù)組的元素是一個自定義的struct類型,在struct中可以通過json tag來定義json的字段名稱。
自定義序列化
如果是自定義的數(shù)據(jù)類型,我們可以實(shí)現(xiàn)Marshaler接口和Unmarshaler接口的方法,這兩個接口的定義如下:
type Unmarshaler interface {
UnmarshalJSON([]byte) error
}
type Marshaler interface {
MarshalJSON() ([]byte, error)
}當(dāng)調(diào)用Marshal函數(shù)序列化時,會調(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ù)反序列化時會調(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只會返回一個字節(jié)數(shù)組,Unmarshal函數(shù)只能傳入一個字節(jié)數(shù)組,如果我們想將序列化的數(shù)據(jù)寫入到不同的地方(比如保存到一個文件中),或者想從不同地方讀取JSON數(shù)據(jù)(比如從一個文件讀取),那么用Encoder和Decoder會更方便。
json.Encoder
NewEncoder函數(shù)可以創(chuàng)建一個Encoder結(jié)構(gòu)體,NewEncoder函數(shù)接收一個實(shí)現(xiàn)了io.Writer接口的參數(shù),在序列化后會調(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高級編程": 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)行后,會把JSON數(shù)據(jù)保存到當(dāng)前目錄的config.json文件當(dāng)中。
json.Decoder
NewDecoder函數(shù)可以創(chuàng)建一個Decoder結(jié)構(gòu)體,NewDecoder函數(shù)接收一個實(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)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
GO項(xiàng)目實(shí)戰(zhàn)之Gorm格式化時間字段實(shí)現(xiàn)
GORM自帶的time.Time類型JSON默認(rèn)輸出RFC3339Nano格式的,下面這篇文章主要給大家介紹了關(guān)于GO項(xiàng)目實(shí)戰(zhàn)之Gorm格式化時間字段實(shí)現(xiàn)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-01-01
使用go語言實(shí)現(xiàn)查找兩個數(shù)組的異同操作
這篇文章主要介紹了使用go語言實(shí)現(xiàn)查找兩個數(shù)組的異同操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12
Golang中Map按照Value大小排序的方法實(shí)例
這篇文章主要給大家介紹了關(guān)于Golang中Map按照Value大小排序的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-03-03
基于Go語言實(shí)現(xiàn)一個并發(fā)端口掃描器
這篇文章主要介紹了如何使用 Go 實(shí)現(xiàn)一個并發(fā)端口掃描器,通過 Goroutine 并發(fā)掃描多個端口,極大地提升端口掃描的效率,本文不僅講解了如何使用 Go 的并發(fā)特性,還涉及了如何處理超時和錯誤,保證端口掃描的健壯性和效率,需要的朋友可以參考下2025-08-08

