golang數據傳輸格式-序列化與反序列化


                  golang數據傳輸格式-序列化與反序列化

                                          作者:尹正傑

版權聲明:原創作品,謝絕轉載!否則將追究法律責任。

 

 

  想必計算機專業畢業的小伙伴應該都知道數據想要持久化存儲,必須將其存在I/O設備里面,這些I/O設備可以是光盤,U盤,機械硬盤,移動硬盤等等。那么這些數據是以哪種方式進程存取的呢?這就是我們聊的數據傳輸格式。

  數據格式(data format)是描述數據保存在文件記錄中的規則。可以是字符形式的文本格式,或二進制數據形式的壓縮格式。字符形式的文本格式占用的存貯空間多但透明度高,二進制數形式的壓縮格式占用的存貯空間少但缺少透明度。數據結構是計算機存儲、組織數據的方式。數據結構是指相互之間存在一種或多種特定關系的數據元素的集合。通常情況下,精心選擇的數據結構可以帶來更高的運行或者存儲效率

  數據結構往往同高效的檢索算法索引技術有關。[1] 數據結構要在網絡中傳輸或保存到文件,就必須對其編碼和解碼;目前存在很多編碼格式:JSON,XML,gob,Google 緩沖協議等等。Go 語言支持所有這些編碼格式。不過本篇博客將討論前三種格式。

  

 

一.XML

  XML作為一種數據交換和信息傳遞的格式已經十分普及。而隨着 Web服務日益廣泛的應用,現在XML在日常的開發工作中也扮演了愈發重要的角色。如同 json 包一樣,也有 Marshal() 和 UnMarshal() 從 XML 中編碼和解碼數據;但這個更通用,可以從文件中讀取和寫入(或者任何實現了 io.Reader 和 io.Writer 接口的類型)和 JSON 的方式一樣,XML 數據可以序列化為結構,或者從結構反序列化為 XML 數據;

  接下來我們將對“yinzhengjie.xml”文件進行反序列化,其文件內容如下:

1 <?xml version="1.0" encoding="UTF-8"?>      <!--第一行是 XML 聲明。它定義 XML 的版本 (1.0) 和所使用的"UTF-8"編碼。-->
2 <UserInformation>                   <!--第一行描述文檔的根元素(像在說:“本文檔是記錄用戶信息的”):-->
3     <string age="18">yinzhengjie</string>   <!--本行和以下兩行都是跟的子元素,當然你可以給它嵌套更多的子子孫孫。注意,這里的標簽名稱是“string”,這個標簽名稱你也可以自己隨意更改的喲!-->
4     <string age="20">尹正傑</string>
5 </UserInformation>                <!--最后一行定義根元素的結尾-->

1.Xml數據格式的反序列化

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import (
11     "encoding/xml"
12     "io/ioutil"
13     "log"
14     "fmt"
15 )
16 
17 var (
18     InputFile = "E:\\Code\\Golang\\Golang_Program\\數據格式進階\\yinzhengjie.xml"
19 )
20 
21 
22 type RootElement struct {                                            //這是定義根元素的,我們需要做的就是講XML數據的結構先定義出來,就是為了方便主函數一會直接通過這個結構去讀取數據。
23     XMLName        xml.Name         `xml:"UserInformation"`     //這個我們指定XML的根元素,我們也可以叫它最外層標簽。
24     ResourceString []ChildElement `xml:"string"`                //上面是指定父標簽的話,這個應該就不用多做解釋吧,當然是子標簽了啦。如果你的子表情有多個屬性的,或是有多個同類型的標簽的話,就可以用切片來存儲。
25 }
26 
27 type ChildElement struct {                    //這個結構體是定義子元素的結構的。
28     XMLName    xml.Name `xml:"string"`        //注意,這里是我們的子元素的一級標簽,因此該標簽的名稱必須填寫正確,不然反序列化的時候就會報錯。
29     StringName string   `xml:"age,attr"`        //這個是定義,一級標簽的屬性的,我們需要用attr來進行表示,於此同時,我們還需要輸入屬性的關鍵字“age”
30     InnerText  string   `xml:",innerxml"`            //這行就是定義標簽里面的具體內容經的,對了,其中關鍵字“,innerxml”里的逗號不要忘記喲。
31 }
32 
33 func main() {
34     GolangXml, err := ioutil.ReadFile(InputFile)    //我們把數據一次性讀取到一個切片中。
35     if err != nil {
36         log.Fatal(err)
37     }
38 
39     var result RootElement
40     fmt.Printf("序列化之前GolangXml = [%v]\n",result)
41     err = xml.Unmarshal(GolangXml, &result)            //然后對數據進行反序列化。
42     if err != nil {
43         log.Fatal(err)
44     }
45     fmt.Printf("序列化之后GolangXml = [%v]\n",result)
46     fmt.Println(result.ResourceString)
47 }
48 
49 
50 
51 #以上代碼執行結果如下:
52 序列化之前GolangXml = [{{ } []}]
53 序列化之后GolangXml = [{{ UserInformation} [{{ string} 18 yinzhengjie} {{ string} 20 尹正傑}]}]
54 [{{ string} 18 yinzhengjie} {{ string} 20 尹正傑}]

2.Xml數據格式的序列化

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import (
11     "encoding/xml"
12     "io/ioutil"
13     "log"
14     "fmt"
15     "strings"
16     "os"
17 )
18 
19 var (
20     InputFile = "E:\\Code\\Golang\\Golang_Program\\數據格式進階\\yinzhengjie.xml"
21     OutputFile = "E:\\Code\\Golang\\Golang_Program\\數據格式進階\\yinzhengjie.xml.bak"
22 )
23 
24 
25 type RootElement struct {                                            //這是定義根元素的,我們需要做的就是講XML數據的結構先定義出來,就是為了方便主函數一會直接通過這個結構去讀取數據。
26     XMLName        xml.Name         `xml:"UserInformation"`     //這個我們指定XML的根元素,我們也可以叫它最外層標簽。
27     ResourceString []ChildElement `xml:"string"`                //上面是指定父標簽的話,這個應該就不用多做解釋吧,當然是子標簽了啦。如果你的子表情有多個屬性的,或是有多個同類型的標簽的話,就可以用切片來存儲。
28 }
29 
30 type ChildElement struct {                    //這個結構體是定義子元素的結構的。
31     XMLName    xml.Name `xml:"string"`        //注意,這里是我們的子元素的一級標簽,因此該標簽的名稱必須填寫正確,不然反序列化的時候就會報錯。
32     StringName string   `xml:"age,attr"`        //這個是定義,一級標簽的屬性的,我們需要用attr來進行表示,於此同時,我們還需要輸入屬性的關鍵字“age”
33     InnerText  string   `xml:",innerxml"`            //這行就是定義標簽里面的具體內容經的,對了,其中關鍵字“,innerxml”里的逗號不要忘記喲。
34 }
35 
36 func main() {
37     GolangXml, err := ioutil.ReadFile(InputFile)    //我們把數據一次性讀取到一個切片中。
38     if err != nil {
39         log.Fatal(err)
40     }
41 
42     var result RootElement
43     err = xml.Unmarshal(GolangXml, &result)            //然后對數據進行反序列化。
44     if err != nil {
45         log.Fatal(err)
46     }
47     for key,value := range result.ResourceString{
48         if strings.EqualFold(value.StringName,"18") {  //只修改屬性值為"18"節點的內部文本innerText
49             result.ResourceString[key].InnerText = "666666666666666"    //注意修改的不是value對象,而是直接使用result中的真實對象
50             fmt.Println("內容修改完畢!")
51         }
52     }
53     XmlOutput,err := xml.MarshalIndent(result,"","")    //保存修改后的內容
54     if err == nil {
55         HeaderBytes := []byte(xml.Header)                          //加入XML頭信息
56         XmlData := append(HeaderBytes,XmlOutput...)                //拼接XML頭和實際XML內容
57         ioutil.WriteFile(OutputFile,XmlData,os.ModeAppend)        //寫入文件
58         fmt.Println(string(XmlData))
59         fmt.Println("文件寫入成功!")
60     }else {
61         fmt.Println(err)
62     }
63 }
64 
65 
66 
67 #以上代碼執行結果如下:
68 內容修改完畢!
69 <?xml version="1.0" encoding="UTF-8"?>
70 <UserInformation><string age="18">666666666666666</string><string age="20">尹正傑</string></UserInformation>
71 文件寫入成功!

  執行以上代碼之后,會生成一個新的文件,即“yinzhengjie.xml.bak”文件。其內容如下:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <UserInformation><string age="18">666666666666666</string><string age="20">尹正傑</string></UserInformation>

  更多關於學習XML知識的話,我推薦兩個學習的網站給大家:

      a>.http://www.xml.org/

      b>.http://www.w3school.com.cn/xml/

 

二.JSON

  JSON(JavaScript Object Notation)是一種輕量級的數據交換格式。人類閱讀和寫作很容易。機器解析和生成很容易。它基於JavaScript編程語言的一個子集 , 標准ECMA-262第3版 - 1999年12月。JSON是完全獨立於語言的文本格式,但是使用C語言家族的程序員熟悉的約定,包括C,C ++,C#,Java,JavaScript,Perl,Python等等。這些屬性使JSON成為理想的數據交換語言。

1.Json數據格式的序列化

   在golang語言中,我們用json.Marshal() 進行序列化。json.Marshal() 的函數簽名是 func Marshal(v interface{}) ([]byte, error)。出於安全考慮,在 web 應用中最好使用 json.MarshalforHTML() 函數,其對數據執行HTML轉碼,所以文本可以被安全地嵌在 HTML <script> 標簽中。序列化是在內存中把數據轉換成指定格式(data -> string),反之亦然(string -> data structure)編碼也是一樣的,只是輸出一個數據流(實現了 io.Writer 接口);解碼是從一個數據流(實現了io.Reader)輸出到一個數據結構。

  JSON 與 Go 類型對應如下:

    a>.bool 對應 JSON 的 booleans;
    b>.float64 對應 JSON 的 numbers;
      c>.string 對應 JSON 的 strings;
      d>.nil 對應 JSON 的 null;

  不是所有的數據都可以編碼為 JSON 類型:只有驗證通過的數據結構才能被編碼:
    a>.JSON 對象只支持字符串類型的 key;要編碼一個 Go map 類型,map 必須是 map[string]T(T是json 包中支持的任何類型);
    b>.Channel,復雜類型和函數類型不能被編碼;
    c>.不支持循環數據結構;它將引起序列化進入一個無限循環;
    d>.指針可以被編碼,實際上是對指針指向的值進行編碼(或者指針是 nil);

  Go 語言的 json 包可以讓你在程序中方便的讀取和寫入 JSON 數據。接下來我們一起看一下golang是如何使用json包的:

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import (
11     "encoding/json"
12     "fmt"
13     "log"
14     "os"
15 )
16 
17 var (
18     OutputFile = "E:\\Code\\Golang\\Golang_Program\\數據格式進階\\yinzhengjie.json"
19 )
20 
21 type TenScenicSpots struct {    //定義10個景區的名稱。
22     FirstScenic string
23     SecondScenic string
24     ThirdScenic string
25     FourthScenic string
26     FifrhScenic    string
27     SixthScenic string
28     SeventhScenic string
29     EigthtScenic string
30     NinthScenic string
31     TenthScenic string
32 }
33 
34 type TouristInformation struct {    //定義游客信息
35     VisitorName string                //游客姓名
36     Nationality string                //游客國籍
37     City string                        //想要去的城市
38     ScenicSpot []*TenScenicSpots    //想要去看的景區
39 
40 }
41 
42 func main() {
43     ChaoyangDistrict := &TenScenicSpots{"中華名族園","北京奧林匹克公園","國家體育館","中國科學技術官","奧林匹克公園網球場","蟹島綠色生態農庄","國家游泳中心(水立方)","中國紫檀博物館","北京歡樂谷","元大都城"}
44     DaxingDistrict := &TenScenicSpots{ "北京野生動物園","男孩子麋鹿苑","中華文化園","留民營生態農場","中國印刷博物館","北普陀影視城","大興濱河森林公園","呀路古熱帶植物園","龐各庄萬畝梨園","西黃垈村"}
45     District := TouristInformation{"尹正傑", "中國", "北京", []*TenScenicSpots{ChaoyangDistrict, DaxingDistrict}}
46     GolangJson, err := json.Marshal(District) //這個步驟就是序列化的過程。json.Marshal方法會返回一個字節數組,即GolangJson,與此同時,District已經是JSON格式的啦。
47     if err != nil {
48         log.Fatal("序列化報錯是:%s",err)
49     }
50     fmt.Printf("JSON format: %s", GolangJson)
51 
52     file, _ := os.OpenFile(OutputFile, os.O_CREATE|os.O_WRONLY, 0)
53     defer file.Close()
54     Write := json.NewEncoder(file)  //創建一個編碼器。
55     err = Write.Encode(District)    //由於District已經被json.Marshal方法處理過了,所以我們直接把JSON格式的District傳給Write寫入器,調用該寫入器的Encode方法可以對JSON格式的數據進行編碼。如果順利的話,我們會得到一個nil參數,否則我們會得到編碼的錯誤信息。
56     if err != nil {
57         log.Println("Error in encoding json")
58     }
59 }
60 
61 
62 
63 
64 #以上代碼執行結果如下:
65 JSON format: {"VisitorName":"尹正傑","Nationality":"中國","City":"北京","ScenicSpot":[{"FirstScenic":"中華名族園","SecondScenic":"北京奧林匹克公園","ThirdScenic":"國家體育館","FourthScenic":"中國科學技術官","FifrhScenic":"奧林匹克公園網球場","SixthScenic":"蟹島綠色生態農庄","SeventhScenic":"國家游泳中心(水立方)","EigthtScenic":"中國紫檀博物館","NinthScenic":"北京歡樂谷","TenthScenic":"元大都城"},{"FirstScenic":"北京野生動物園","SecondScenic":"男孩子麋鹿苑","ThirdScenic":"中華文化園","FourthScenic":"留民營生態農場","FifrhScenic":"中國印刷博物館","SixthScenic":"北普陀影視城","SeventhScenic":"大興濱河森林公園","EigthtScenic":"呀路古熱帶植物園","NinthScenic":"龐各庄萬畝梨園","TenthScenic":"西黃垈村"}]}

2.Json數據格式的反序列化    

   如果我們事先知道 JSON 數據,我們可以定義一個適當的結構並對 JSON 數據反序列化。我們上面做序列化的時候創建了一個“yinzhengjie.json”的文件,里面的數據結果我們是心知肚明的,因此,接下來反序列化就是一件很Easy的事情啦。

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 
11 import (
12     "encoding/json"
13     "fmt"
14     "log"
15     "io/ioutil"
16 )
17 
18 var (
19     InputFile = "E:\\Code\\Golang\\Golang_Program\\數據格式進階\\yinzhengjie.json"
20 )
21 
22 type TenScenicSpots struct {    //定義10個景區的名稱。
23     FirstScenic string
24     SecondScenic string
25     ThirdScenic string
26     FourthScenic string
27     FifrhScenic    string
28     SixthScenic string
29     SeventhScenic string
30     EigthtScenic string
31     NinthScenic string
32     TenthScenic string
33 }
34 
35 type TouristInformation struct {    //定義游客信息
36     VisitorName string                //游客姓名
37     Nationality string                //游客國籍
38     City string                        //想要去的城市
39     ScenicSpot []*TenScenicSpots    //想要去看的景區
40 
41 }
42 
43 func main() {
44     var GolangJson   TouristInformation        //定義反序列化JSON的格式
45 
46     file,err := ioutil.ReadFile(InputFile)  //得到的文件是一個字節切片喲
47     if err != nil {
48         log.Println(err)
49     }
50     
51     err = json.Unmarshal(file,&GolangJson)  //將得到的字節進行反序列化
52     if err != nil {
53         log.Fatal("反序列化報錯啦:%s",err)
54     }
55 
56     fmt.Printf("JSON format: %v\n", GolangJson) //將反序列化的文件打印出來。
57     fmt.Println(GolangJson.City)
58     fmt.Println(GolangJson.ScenicSpot[1])
59 }
60 
61 
62 
63 #以上代碼執行結果如下:
64 JSON format: {尹正傑 中國 北京 [0xc042050140 0xc042050280]}
65 北京
66 &{北京野生動物園 男孩子麋鹿苑 中華文化園 留民營生態農場 中國印刷博物館 北普陀影視城 大興濱河森林公園 呀路古熱帶植物園 龐各庄萬畝梨園 西黃垈村}

  更多關於學習XML知識的話,我推薦兩個學習的網站給大家:

      a>.http://www.json.org/

      b>.http://www.w3school.com.cn/json/

 

三.Gob

  Gob 是 Go 自己的以二進制形式序列化和反序列化程序數據的格式;可以在 encoding 包中找到。這種格式的數據簡稱為 Gob (即 Go binary 的縮寫)。類似於 Python 的 "pickle" 和 Java 的"Serialization"。

  Gob 通常用於遠程方法調用參數和結果的傳輸,以及應用程序和機器之間的數據傳輸。 它和 JSON 或 XML 有什么不同呢?Gob 特定地用於純 Go 的環境中,例如,兩個用 Go 寫的服務之間的通信。這樣的話服務可以被實現得更加高效和優化。 Gob 不是可外部定義,語言無關的編碼方式。因此它的首選格式是二進制,而不是像 JSON 和 XML 那樣的文本格式。 Gob 並不是一種不同於Go 的語言,而是在編碼和解碼過程中用到了 Go 的反射。

  Gob 文件或流是完全自描述的:里面包含的所有類型都有一個對應的描述,並且總是可以用 Go 解碼,而不需要了解文件的內容。

  只有可導出的字段會被編碼,零值會被忽略。在解碼結構體的時候,只有同時匹配名稱和可兼容類型的字段才會被解碼。當源數據類型增加新字段后,Gob 解碼客戶端仍然可以以這種方式正常工作:解碼客戶端會繼續識別以前存在的字段。並且還提供了很大的靈活性,比如在發送者看來,整數被編碼成沒有固定長度的可變長度,而忽略具體的 Go 類型。

   Golang的解碼和編碼就相對簡單啦,就我個人而言,我對golang專有的gob格式還是那么的情有獨鍾。它的使用方法很簡單,我們可以一起看一下它的解碼和編碼的過程,具體代碼 如下:

1.Gob的編碼(序列化)

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import (
11     "bytes"
12     "encoding/gob"
13     "log"
14     "io/ioutil"
15 )
16 
17 var (
18     OutputFile = "E:\\Code\\Golang\\Golang_Program\\數據格式進階\\yinzhengjie.gob"
19 )
20 
21 type P struct {
22     X, Y, Z int
23     Name    string
24 }
25 
26 func main() {
27     var GolngGob bytes.Buffer
28 
29     enc := gob.NewEncoder(&GolngGob) //生成一個的編碼器
30 
31     yzj := P{100, 200, 300, "Yinzhengjie"}
32     err := enc.Encode(yzj)    //編碼結構體和數據
33     if err != nil {
34         log.Fatal("encode error:", err)
35     }
36     ioutil.WriteFile(OutputFile, GolngGob.Bytes(), 0644)        //我們把編碼后的數據寫入文件
37 }

  執行以上代碼之后會生成一個“yinzhengjie.gob”文件,這個文件的內容是二進制編碼的,因此我們用gbk編碼或是utf-8編碼格式直接去打開的話可能不是很理想。因此我們可以通過gob的反序列化來進行讀取操作,具體代碼請參考以下代碼。

2.Gob的解碼(反序列化)

 1 /*
 2 #!/usr/bin/env gorun
 3 @author :yinzhengjie
 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/
 5 EMAIL:y1053419035@qq.com
 6 */
 7 
 8 package main
 9 
10 import (
11     "encoding/gob"
12     "log"
13     "os"
14     "fmt"
15 )
16 
17 var (
18     InputFile = "E:\\Code\\Golang\\Golang_Program\\數據格式進階\\yinzhengjie.gob"
19 )
20 
21 type P struct {
22     X, Y, Z int
23     Name    string
24 }
25 
26 type Q struct {
27     X, Y,Z*int32
28     Name string
29 }
30 
31 func main() {
32     file,err := os.Open(InputFile)
33     dec := gob.NewDecoder(file)        //生成一個解碼器
34 
35     var GolangGob Q
36     fmt.Printf("解碼前GolangGob的內容為[%v]\n",GolangGob)
37     err = dec.Decode(&GolangGob)    //開始按照我們定義好的GolangGob結構體開始解碼。
38     if err != nil {
39         log.Fatal("decode error:", err)
40     }
41     fmt.Printf("解碼后GolangGob的內容為[%v]\n",GolangGob)
42 
43     fmt.Printf("詳細內容為:【%d,%d,%d,%q】\n", *GolangGob.X, *GolangGob.Y, *GolangGob.Z, GolangGob.Name)    //注意傳參的順序喲!
44 }
45 
46 
47 
48 #以上代碼執行結果如下:
49 解碼前GolangGob的內容為[{<nil> <nil> <nil> }]
50 解碼后GolangGob的內容為[{0xc04203c838 0xc04203c83c 0xc04203c840 Yinzhengjie}]
51 詳細內容為:【100,200,300,"Yinzhengjie"

 

四.數據傳輸進階知識-golang中的密碼學

 通過網絡傳輸的數據必須加密,以防止被 hacker(黑客)讀取或篡改,並且保證發出的數據和收到的數據檢驗和一致。 鑒於 Go 母公司的業務,我們毫不驚訝地看到 Go 的標准庫為該領域提供了超過 30 個包:

  1>.hash 包:實現了 adler32 、 crc32 、 crc64 和 fnv 校驗;

  2>.crypto 包:實現了其它的 hash 算法,比如 md4 、 md5 、 sha1 等。以及完整地實現了 aes 、blowfish 、 rc4 、 rsa 、 xtea 等加密算法。

  想要了解更多關於golang加密方法的可以參考:http://www.cnblogs.com/yinzhengjie/p/7368030.html

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM