Go語言中的數(shù)據(jù)格式(json、xml?、msgpack、protobuf)使用總結(jié)
在分布式的系統(tǒng)中,因為涉及到數(shù)據(jù)的傳輸,所以一定會進行數(shù)據(jù)的交換,此時就要定義數(shù)據(jù)交換的格式,例如二進制、Json、Xml等等。本篇文章就是總結(jié)一下常用的幾種數(shù)據(jù)格式。
一、Json格式
如果想使用Json數(shù)據(jù)格式,可以借助于encoding/json這個包。
利用json包里的 json.Marshal(xxx) 和 json.Unmarshal(data, &xxx) 進行序列化和反序列化。
下面舉個例子:
package main import ( "encoding/json" "fmt" "io/ioutil" "math/rand" ) type Student struct { Name string Age int Sex string } //寫入json數(shù)據(jù) func writeJson(filename string) (err error) { var students []*Student //隨機生成10個學(xué)生數(shù)據(jù) for i := 0; i < 10; i++ { p := &Student{ Name: fmt.Sprintf("name%d", i), Age: rand.Intn(100), Sex: "Man", } students = append(students, p) } //執(zhí)行序列化操作 data, err := json.Marshal(students) if err != nil { fmt.Printf("=marshal failed, err:%v\n", err) return } //將數(shù)據(jù)寫到一個文件當(dāng)中 err = ioutil.WriteFile(filename, data, 0755) if err != nil { fmt.Printf("write file failed, err:%v\n", err) return } return } //讀取json數(shù)據(jù) func readJson(filename string) (err error) { var students []*Student data, err := ioutil.ReadFile(filename) if err != nil { return } err = json.Unmarshal(data, &students) if err != nil { return } for _, v := range students { fmt.Printf("%#v\n", v) } return }
執(zhí)行:
func main() { filename := "C:/tmp/Students.txt" err := writeJson(filename) if err != nil { fmt.Printf("write json failed, err:%v\n", err) return } err = readJson(filename) if err != nil { fmt.Printf("read json failed, err:%v\n", err) return } }
執(zhí)行結(jié)果:
- 1.可以看到在C:/tmp/下面生成了一個Students.txt文件,打開里面存放是剛剛隨機生成的10個學(xué)生數(shù)據(jù)
- 2.執(zhí)行結(jié)果可以看到控制臺打?。?/li>
二、Xml格式
Xml格式也是我們常用的數(shù)據(jù)格式,同樣要使用Xml格式,可以使用encoding/xml這個包。
像上面json一樣,同樣存在 xml.Marshal(xxx) 和 xml.Unmarshal(data, &xxx) 兩個方法。此外還有方法xml.MarshalIndent(xxx) 可以格式化xml
先熟悉一下XML對應(yīng) 標(biāo)簽怎么寫:
- - XMLName字段,如上所述,會省略
- - 具有標(biāo)簽"-"的字段會省略
- - 具有標(biāo)簽"name,attr"的字段會成為該XML元素的名為name的屬性
- - 具有標(biāo)簽",attr"的字段會成為該XML元素的名為字段名的屬性
- - 具有標(biāo)簽",chardata"的字段會作為字符數(shù)據(jù)寫入,而非XML元素
- - 具有標(biāo)簽",innerxml"的字段會原樣寫入,而不會經(jīng)過正常的序列化過程
- - 具有標(biāo)簽",comment"的字段作為XML注釋寫入,而不經(jīng)過正常的序列化過程,該字段內(nèi)不能有"--"字符串
- - 標(biāo)簽中包含"omitempty"選項的字段如果為空值會省略
空值為false、0、nil指針、nil接口、長度為0的數(shù)組、切片、映射 - - 匿名字段(其標(biāo)簽無效)會被處理為其字段是外層結(jié)構(gòu)體的字段
- - 如果一個字段的標(biāo)簽為"a>b>c",則元素c將會嵌套進其上層元素a和b中。如果該字段相鄰的字段標(biāo)簽指定了同樣的上層元素,則會放在同一個XML元素里。
- - 如果一個字段的標(biāo)簽為"a>b>c",則元素c將會嵌套進其上層元素a和b中。如果該字段相鄰的字段標(biāo)簽指定了同樣的上層元素,則會放在同一個XML元素里。
下面舉個例子:
例如我想創(chuàng)建一個如下的xml數(shù)據(jù):
<Servers version="2.0"> <server> <serverName>Server0</serverName> <serverIP>192.168.1.0</serverIP> </server> <server> <serverName>Server1</serverName> <serverIP>192.168.1.1</serverIP> </server> </Servers>
我就可以創(chuàng)建下面這樣的結(jié)構(gòu)體:
//最外層的xml type Servers struct { XMLName xml.Name `xml:"Servers"` Version string `xml:"version,attr"` Servers []*Server `xml:"server"` } //具體的server type Server struct { ServerName string `xml:"serverName"` ServerIP string `xml:"serverIP"` }
寫文件方法:
func writeXml(fileName string) (err error) { //創(chuàng)建一個*Server類型的數(shù)組 var serverList []*Server for i := 0; i < 2; i++ { s := &Server{ ServerName: fmt.Sprintf("Server%d", i), ServerIP: fmt.Sprintf("192.168.1.%d", i), } serverList = append(serverList, s) } var myServers *Servers = &Servers{ Version: "2.0", Servers: serverList, } //執(zhí)行序列化操作 data, err := xml.MarshalIndent(myServers, "", " ") if err != nil { fmt.Printf("=marshal failed, err:%v\n", err) return } //將數(shù)據(jù)寫到一個文件當(dāng)中 err = ioutil.WriteFile(fileName, data, 0755) if err != nil { fmt.Printf("write file failed, err:%v\n", err) return } return }
如上代碼,使用了MarshalIndent方法,第一個參數(shù)是需要序列化的數(shù)據(jù),第二參數(shù)是前綴,第三個是縮進的字符串(這里是四個空格),然后在main方法中調(diào)用一下即可(代碼略)。
這里主要想說明一下結(jié)構(gòu)體里面的標(biāo)簽:
XmlName可以省略不寫,不寫的話最外層就是用的結(jié)構(gòu)體的名稱,例如第一個結(jié)構(gòu)體是Servers,那么xml最外層的節(jié)點名稱就是Servers。
讀的話,使用 xml.Unmarshal(data, &xxx) 就可以實現(xiàn)了。
func readXml(fileName string) (err error) { var myServers *Servers data, err := ioutil.ReadFile(fileName) if err != nil { return } err = xml.Unmarshal(data, &myServers) if err != nil { return } fmt.Printf("XMLNAME = %v\n", myServers.XMLName) fmt.Printf("Version = %v\n", myServers.Version) for _, v := range myServers.Servers { fmt.Printf("%v\n", v) } return }
三、msgPack格式
上面兩種Json和Xml格式,都是文本格式的數(shù)據(jù),好處在于能夠方便的閱讀。但是問題在于占用空間比較大。所以又出現(xiàn)了MsgPack這種格式,它是在json基礎(chǔ)上轉(zhuǎn)換為二進制進行傳輸?shù)?。對?yīng)關(guān)系像下面這個圖:
MsgPack并沒有官方的包,我們需要使用一個第三方的包,項目地址:https://github.com/vmihailenco/msgpack
實現(xiàn)比較簡單,將 json.Marshal 和 json.Unmarshal 中的【 json】替換為【 maspack】即可,下面是對上面代碼的改造,創(chuàng)建了10000個學(xué)生的數(shù)據(jù)。
四、protobuf格式
protobuf是Google公司開發(fā)出的一種數(shù)據(jù)格式。官方文檔地址:https://developers.google.cn/protocol-buffers/ 。
簡單講它使用了IDL語言作為中間語言來串聯(lián)不同的編程語言。不同的語言可以根據(jù)生成的IDL中間語言,生成自己的語言。
這樣做有什么好處? 舉個例子:當(dāng)我們在協(xié)作開發(fā)的時候,A部門使用的是Go語言、B部分使用的是Java語言,C部門使用的是C#語言,當(dāng)他們之間進行數(shù)據(jù)交換的時候,都要各自維護自己的結(jié)構(gòu)體,才能進行數(shù)據(jù)的
序列化和反序列化,使用protobuf的好處就是只需要一個IDL描述,然后生成不同的語言的結(jié)構(gòu),這樣維護一份就可以了。
同時 prototbuf的性能也很好,這也是它的一個優(yōu)勢。IDL語言使用的變長編碼(根據(jù)整數(shù)的范圍 0-255 那么這個數(shù)字就占用1個字節(jié) ,如果使用定長編碼的話 一個整數(shù)可能就是 4個字節(jié))所以它的空間利用率是很好的。
那開發(fā)流程是怎樣的?
- A. IDL編寫
- B. 生成只定語言的代碼
- C. 序列化和反序列化
如何在Go中應(yīng)用prototbuf
A.安裝protoc編譯器
解壓后拷貝到GOPATH/bin目錄下, 下載地址:https://github.com/google/protobuf/releases
然后把bin下面的protoc.exe 這個放到GoPath下的bin中,打開cmd,輸入protoc,應(yīng)該會出現(xiàn)如下內(nèi)容:
如果不存在,可以將Gopath的bin加入到系統(tǒng)的環(huán)境變量path當(dāng)中。
B.安裝生成Go語言的插件
執(zhí)行命令:
go get -u github.com/golang/protobuf/protoc-gen-go
C. 創(chuàng)建一個簡單的proto文件
//指定版本 //注意proto3與proto2的寫法有些不同 syntax = "proto3"; //包名,通過protoc生成時go文件時 package school; //性別 //枚舉類型第一個字段必須為0 enum Sex { male = 0; female = 1; other =2; } //學(xué)生 message Student { Sex sex = 1; string Name = 2; int32 Age =3; } //班級 message Class{ repeated Student Students =1; string Name; }
message 就可以理解成類, repeated可以理解成數(shù)組。
D.利用之前下載好的protoc.exe 生成一個Go的代碼。
第一個【.】代表當(dāng)前輸出的目錄,后面*.proto則是 proto文件的路徑
protoc--go_out=. *.proto
protoc --go_out=.\school\ .\school.proto
執(zhí)行之后會生成如下的文件,這個go文件就可以直接使用了。
E. 使用生成的Go文件
①使用 proto.Marshal() 執(zhí)行序列化
func writeProto(filename string) (err error) { //創(chuàng)建學(xué)生信息 var students []*school.Student for i := 0; i < 30; i++ { var sex = (school.Sex)(i % 3) student := &school.Student{ Name: fmt.Sprintf("Student_%d", i), Age: 15, Sex: sex, } students = append(students, student) } //創(chuàng)建班級信息 var myClass school.Class myClass.Name = "我的班級" myClass.Students = students data, err := proto.Marshal(&myClass) if err != nil { fmt.Printf("marshal proto buf failed, err:%v\n", err) return } err = ioutil.WriteFile(filename, data, 0755) if err != nil { fmt.Printf("write file failed, err:%v\n", err) return } return }
②使用proto.Unmarshal(data, &mySchool)執(zhí)行反序列化
func readProto(filename string) (err error) { var mySchool school.Class data, err := ioutil.ReadFile(filename) if err != nil { return } err = proto.Unmarshal(data, &mySchool) if err != nil { return } fmt.Printf("proto:%v\n", mySchool) return }
Q&A
如果在使用protobuf生成的Go文件,出現(xiàn)了如下的異常:
undefined: proto.ProtoPackageIsVersion3
這個時候可能是由于上面兩步下載的protoc.exe 和 protobuf 的版本不一致導(dǎo)致的。
- 1. 可以清空下gopath下的 github.com\golang\protobuf 然后重新下載,并在github.com\golang\protobuf\protoc-gen-go 執(zhí)行 go install 命令。
- 2. 檢查一下是不是使用了 godep 等包管理工具,里面引用的版本和protoc.exe 不一致造成的
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請查看下面相關(guān)鏈接
相關(guān)文章
Golang性能提升利器之SectionReader的用法詳解
本文將介紹 Go 語言中的 SectionReader,包括 SectionReader的基本使用方法、實現(xiàn)原理、使用注意事項,感興趣的小伙伴可以了解一下2023-07-07Go語言中利用http發(fā)起Get和Post請求的方法示例
這篇文章主要給大家介紹了關(guān)于Go語言中利用http發(fā)起Get和Post請求的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。2017-11-11淺析Go語言如何在select語句中實現(xiàn)優(yōu)先級
這篇文章主要為大家詳細(xì)介紹了Go語言如何在select語句中實現(xiàn)優(yōu)先級,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03解析golang 標(biāo)準(zhǔn)庫template的代碼生成方法
這個項目的自動生成代碼都是基于 golang 的標(biāo)準(zhǔn)庫 template 的,所以這篇文章也算是對使用 template 庫的一次總結(jié),本文通過實例代碼給大家介紹的非常詳細(xì),需要的朋友參考下吧2021-11-11Go語言基礎(chǔ)語法和基本數(shù)據(jù)類型知識鞏固
這篇文章主要為大家介紹了Go語言基礎(chǔ)語法和基本數(shù)據(jù)類型知識鞏固,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11