Go語(yǔ)言中json操作的實(shí)現(xiàn)
?? 一、JSON 與 Go 類型對(duì)應(yīng)關(guān)系
理解 JSON 類型與 Go 類型的映射是處理 JSON 的基礎(chǔ):
JSON 類型 | Go 類型 | 說(shuō)明 |
---|---|---|
對(duì)象 (object) | struct, map[string]T | 鍵值對(duì)集合 |
數(shù)組 (array) | []T (切片), [n]T (數(shù)組) | 值的有序集合,通常使用切片更靈活 |
字符串 (string) | string | |
數(shù)字 (number) | int, float64 等 | 注意:JSON 中的數(shù)字默認(rèn)解碼為 float64 |
布爾 (boolean) | bool | |
null | nil |
??? 二、基本操作:編碼與解碼
Go 使用標(biāo)準(zhǔn)庫(kù) encoding/json
進(jìn)行 JSON 處理。
編碼 (序列化,Marshal):使用 json.Marshal
將 Go 數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換為 JSON 字節(jié)切片 ([]byte
)。
package main import ( "encoding/json" "fmt" ) type Person struct { Name string `json:"name"` // 通過(guò)標(biāo)簽指定JSON字段名 Age int `json:"age"` } func main() { p := Person{Name: "Alice", Age: 30} jsonBytes, err := json.Marshal(p) if err != nil { panic(err) } fmt.Println(string(jsonBytes)) // 輸出:{"name":"Alice","age":30} }
解碼 (反序列化,Unmarshal):使用 json.Unmarshal
將 JSON 字節(jié)切片解析回 Go 數(shù)據(jù)結(jié)構(gòu)。
jsonStr := `{"name":"Bob","age":25}` var p2 Person err = json.Unmarshal([]byte(jsonStr), &p2) // 注意傳遞指針 if err != nil { panic(err) } fmt.Printf("%+v\n", p2) // 輸出:{Name:Bob Age:25}
格式化輸出:使用 json.MarshalIndent
生成帶縮進(jìn)的格式化 JSON,便于閱讀。
jsonBytes, err := json.MarshalIndent(p, "", " ") // 前綴為空,縮進(jìn)為兩個(gè)空格 if err != nil { panic(err) } fmt.Println(string(jsonBytes)) /* 輸出: { "name": "Alice", "age": 30 } */
?? 三、結(jié)構(gòu)體標(biāo)簽(Struct Tags)的運(yùn)用
結(jié)構(gòu)體標(biāo)簽是控制 JSON 序列化和反序列化行為的關(guān)鍵。
自定義字段名:使用 json:"field_name"
指定 JSON 字段名。
type Product struct { ProductID int `json:"id"` // JSON中字段名為 "id" Name string `json:"name"` // JSON中字段名為 "name" }
忽略字段:
type User struct { Name string `json:"name"` Password string `json:"-"` // 序列化時(shí)忽略密碼字段 }
type User struct { Name string `json:"name"` Email string `json:"email,omitempty"` // 如果Email為空,則序列化時(shí)不包含此字段 Age int `json:"age,omitempty"` // 如果Age為0,則序列化時(shí)不包含此字段 }
- 永久忽略:使用
json:"-"
,該字段不會(huì)參與 JSON 序列化。 - 條件忽略(空值):使用
omitempty
,當(dāng)字段為零值時(shí)(如空字符串、零值、nil指針等),序列化時(shí)省略該字段。
字符串形式的數(shù)字:有時(shí)需要將數(shù)字類型以字符串形式序列化,或從字符串形式的數(shù)字反序列化,可以使用 ,string
標(biāo)簽選項(xiàng)。
type MyData struct { Num int `json:"num,string"` // 序列化為 {"num": "123"}, 從 {"num": "123"} 反序列化 }
注意:此標(biāo)簽僅適用于 int
, float
, bool
等類型與字符串形式的互轉(zhuǎn),且 JSON 中對(duì)應(yīng)的值必須是帶引號(hào)的字符串。
?? 四、處理未知結(jié)構(gòu)或動(dòng)態(tài) JSON
當(dāng)你無(wú)法預(yù)知 JSON 的確切結(jié)構(gòu)時(shí),有兩種主要方法:
使用 map[string]interface{}
:適合快速訪問(wèn),但需要類型斷言,安全性較低。
jsonStr := `{"name":"Alice","age":30,"hobbies":["reading","traveling"]}` var data map[string]interface{} err := json.Unmarshal([]byte(jsonStr), &data) if err != nil { panic(err) } name := data["name"].(string) // 類型斷言 age := data["age"].(float64) // JSON數(shù)字默認(rèn)轉(zhuǎn)為float64 hobbies := data["hobbies"].([]interface{}) fmt.Println(name, age, hobbies[0])
使用 json.RawMessage
:延遲解析,允許你先處理已知部分,再按需解析未知部分,更靈活安全。
type Message struct { Name string `json:"name"` Extra json.RawMessage `json:"extra"` // 原始JSON字節(jié),暫不解析 } var msg Message json.Unmarshal([]byte(jsonStr), &msg) // 之后根據(jù)條件再解析 Extra 字段
? 五、處理時(shí)間類型(time.Time)
time.Time
類型默認(rèn)序列化為 RFC 3339 格式的字符串。你也可以為 time.Time
定義自定義的 Marshaler
和 Unmarshaler
接口實(shí)現(xiàn),以適應(yīng)特定的時(shí)間格式。
type Event struct { Name string `json:"name"` Timestamp time.Time `json:"timestamp"` // 默認(rèn)格式:2006-01-02T15:04:05Z07:00 } event := Event{Name: "Party", Timestamp: time.Now()} jsonData, _ := json.Marshal(event) fmt.Println(string(jsonData)) // {"name":"Party","timestamp":"2023-10-01T10:00:00Z"}
?? 六、流式編碼與解碼
處理大型 JSON 數(shù)據(jù)或 IO 流(如 HTTP 請(qǐng)求體、文件)時(shí),使用 json.Encoder
和 json.Decoder
可以提高內(nèi)存效率。
流式編碼(寫(xiě)入)
file, err := os.Create("output.json") if err != nil { panic(err) } defer file.Close() encoder := json.NewEncoder(file) encoder.SetIndent("", " ") // 設(shè)置縮進(jìn) err = encoder.Encode(data) // 將數(shù)據(jù)直接編碼并寫(xiě)入文件 if err != nil { panic(err) }
流式解碼(讀取)
file, err := os.Open("large_data.json") if err != nil { panic(err) } defer file.Close() decoder := json.NewDecoder(file) for { var item MyStruct if err := decoder.Decode(&item); err == io.EOF { break // 流結(jié)束 } else if err != nil { panic(err) } // 處理每一個(gè) item fmt.Println(item) }
?? 七、自定義序列化與反序列化
通過(guò)實(shí)現(xiàn) json.Marshaler
和 json.Unmarshaler
接口,你可以完全控制特定類型的編碼和解碼邏輯。
type Status int const ( Unknown Status = iota Active Inactive ) // 實(shí)現(xiàn) MarshalJSON,將 Status 轉(zhuǎn)換為字符串 func (s Status) MarshalJSON() ([]byte, error) { var str string switch s { case Active: str = "active" case Inactive: str = "inactive" default: str = "unknown" } return json.Marshal(str) } // 實(shí)現(xiàn) UnmarshalJSON,將字符串轉(zhuǎn)換回 Status func (s *Status) UnmarshalJSON(data []byte) error { var str string if err := json.Unmarshal(data, &str); err != nil { return err } switch str { case "active": *s = Active case "inactive": *s = Inactive default: *s = Unknown } return nil }
? 八、性能優(yōu)化建議
- 避免使用 interface{}:盡可能使用明確類型的結(jié)構(gòu)體,而不是 map[string]interface{},這能減少反射開(kāi)銷(xiāo)并提高類型安全。
- 重用 json.Decoder:在循環(huán)中解碼多個(gè) JSON 對(duì)象時(shí),復(fù)用 json.Decoder 實(shí)例。
- 預(yù)分配切片:如果你知道解碼后數(shù)組的大致大小,預(yù)分配切片容量可以減少擴(kuò)容帶來(lái)的開(kāi)銷(xiāo)。
- **使用 json.RawMessage`` 延遲解析**:對(duì)于部分不需要立即處理的 JSON 數(shù)據(jù),可以先存儲(chǔ)為 json.RawMessage`,等到需要時(shí)再解析,從而減少不必要的解析開(kāi)銷(xiāo)。
?? 總結(jié)與建議
Go 語(yǔ)言的 encoding/json 庫(kù)提供了強(qiáng)大而靈活的 JSON 處理能力。掌握從基礎(chǔ)編碼解碼到高級(jí)技巧,能讓你更自如地應(yīng)對(duì)各種數(shù)據(jù)處理場(chǎng)景。記住,明確的結(jié)構(gòu)體定義優(yōu)于 map[string]interface{},善用結(jié)構(gòu)體標(biāo)簽?zāi)芫?xì)控制序列化行為,而 流式處理 則是處理大數(shù)據(jù)的利器。
到此這篇關(guān)于Go語(yǔ)言中json操作的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go語(yǔ)言 json操作內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
通過(guò)Go channel批量讀取數(shù)據(jù)的示例詳解
批量處理的主要邏輯是:從 channel 中接收數(shù)據(jù),積累到一定數(shù)量或者達(dá)到時(shí)間限制后,將數(shù)據(jù)批量處理(例如發(fā)送到 Kafka 或者寫(xiě)入網(wǎng)絡(luò)),下面我將展示一個(gè)從 Go channel 中批量讀取數(shù)據(jù),并批量發(fā)送到 Kafka 和批量寫(xiě)入網(wǎng)絡(luò)數(shù)據(jù)的示例,需要的朋友可以參考下2024-10-10解決golang編譯提示dial tcp 172.217.160.113:443: con
這篇文章主要介紹了解決golang編譯提示dial tcp 172.217.160.113:443: connectex: A connection attempt failed,此問(wèn)題完美解決,需要的朋友可以參考下2023-02-02使用Golong實(shí)現(xiàn)JWT身份驗(yàn)證的詳細(xì)過(guò)程
JWT提供了一種強(qiáng)大而靈活的方法來(lái)處理Web應(yīng)用程序中的身份驗(yàn)證和授權(quán),本教程將引導(dǎo)您逐步實(shí)現(xiàn)Go應(yīng)用程序中的JWT身份驗(yàn)證過(guò)程,感興趣的朋友跟隨小編一起看看吧2024-03-03Go語(yǔ)言for-range函數(shù)使用技巧實(shí)例探究
這篇文章主要為大家介紹了Go語(yǔ)言for-range函數(shù)使用技巧實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01Mac下Vs code配置Go語(yǔ)言環(huán)境的詳細(xì)過(guò)程
這篇文章給大家介紹Mac下Vs code配置Go語(yǔ)言環(huán)境的詳細(xì)過(guò)程,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2021-07-07Go語(yǔ)言如何使用分布式鎖解決并發(fā)問(wèn)題
這篇文章主要為大家詳細(xì)介紹了Go 語(yǔ)言生態(tài)中基于 Redis 實(shí)現(xiàn)的分布式鎖庫(kù) redsync,并探討其使用方法和實(shí)現(xiàn)原理,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-03-03go使用snmp庫(kù)查詢mib數(shù)據(jù)案例代碼
go語(yǔ)言使用snmp庫(kù)中的 k-sone/snmpgo 實(shí)現(xiàn)相關(guān)mib查詢,本文通過(guò)實(shí)例代碼給大家介紹了go使用snmp庫(kù)查詢mib數(shù)據(jù),感興趣的朋友跟隨小編一起看看吧2023-10-10