Golang Gob編碼

gob是Golang包自帶的一個數據結構序列化的編碼/解碼工具。編碼使用Encoder,解碼使用Decoder。一種典型的應用場景就是RPC(remote procedure calls)。html

gob和json的pack之類的方法同樣,由發送端使用Encoder對數據結構進行編碼。在接收端收到消息以後,接收端使用Decoder將序列化的數據變化成本地變量。golang

 

有一點須要注意,json

發送方的結構和接受方的結構並不須要徹底一致

結構體中缺省的字段將不會被髮送。並且在接收方,並不須要全部的字段都要有對應的結構屬性對應。godoc中的這個例子很形象:數據結構

clip_image001

當發送方傳遞的是struct{A, B int}結構的值的時候,接收方能夠容許前9種結構,可是後面4種結構確實不容許的。函數

 

我的以爲這種設定是很符合邏輯的:接收端只接受和發送數據「類似」的數據結構。容許模擬類似,可是不容許矛盾。工具

 

各個類型的編解碼規則

整型:分爲sign int和usign int, 其中從上面例子也看到,int和uint是不能互相編解碼的。float和int也是不能互相編解碼的。ui

Struct,array,slice是能夠被編碼的。可是function和channel是不能被編碼的。this

bool類型是被看成uint來編碼的,0是false,1是true。編碼

浮點類型的值都是被看成float64類型的值來編碼的spa

String和[]byte傳遞是uint(byte個數) + byte[]的形式編碼的

Slice和array是按照uint(array個數) + 每一個array編碼 這樣的形式進行編碼的

Maps是按照 uint(Map個數) + 鍵值對 這樣的形式進行編碼的

 

Struct是按照一對對(屬性名 + 屬性值)來進行編碼的。其中屬性值是其本身對應的gob編碼。前面說過,若是有一個屬性值爲0或空,則這個屬性直接被忽略。每一個屬性的序號是由編碼時候順序決定的,從0開始順序遞增。Struct在序列化前會以-1表明序列化的開始,以0表明序列化結束。即Struct的序列化是按照 「-1 (0 屬性1名字 屬性1值) (1 屬性2名字 屬性2值) 0 」來進行編碼的。

 

很是重要的一點:

Struct中的屬性應該是public的,即應該是大寫字母開頭。

這樣才能被包外的函數訪問!!(謝謝TreapDB提醒)

Gob提供的函數

clip_image002

 

Encode和Decode

對於Encoder和Decoder能夠看這個例子:

package main

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"log"
)

type P struct {
	X, Y, Z int
	Name    string
}

type Q struct {
	X, Y *int32
	Name string
}

func main() {
	var network bytes.Buffer        
	enc := gob.NewEncoder(&network) 
	dec := gob.NewDecoder(&network) 
	// Encode (send) the value.
	err := enc.Encode(P{3, 4, 5, "Pythagoras"})
	if err != nil {
		log.Fatal("encode error:", err)
	}
	// Decode (receive) the value.
	var q Q
	err = dec.Decode(&q)
	if err != nil {
		log.Fatal("decode error:", err)
	}
	fmt.Println(q)
	fmt.Printf("%q: {%d,%d}\n", q.Name, *q.X, *q.Y)

}

全部Encoder和Decoder的構造函數都有一個io結構,須要制定你將使用哪一個io進行編碼解碼的傳輸。

這個代碼要注意幾個地方:

1 P和Q是兩個結構體,應該說是「類似」的兩個結構體

2 Encode是將結構體傳遞過來,可是Decode的函數參數倒是一個pointer!

這點在godoc中有說:

f e is nil, the value will be discarded. Otherwise, the value underlying e must be a pointer to the correct type for the next data item received.

Decode的參數若是不是nil,那就必定是一個指針了。

3 若是你將Encode傳入一個pointer,即

func main() {
	var network bytes.Buffer        // Stand-in for a network connection
	enc := gob.NewEncoder(&network) // Will write to network.
	dec := gob.NewDecoder(&network) // Will read from network.
	// Encode (send) the value.
	err := enc.Encode(&P{3, 4, 5, "Pythagoras"})
	if err != nil {
		log.Fatal("encode error:", err)
	}
	// Decode (receive) the value.
	var q Q
	err = dec.Decode(&q)
	if err != nil {
		log.Fatal("decode error:", err)
	}
	fmt.Println(q)
	fmt.Printf("%q: {%d,%d}\n", q.Name, *q.X, *q.Y)


}

這個function也是沒有問題的。

Register和RegisterName

這兩個方法是當編解碼中有一個字段是interface{}的時候須要對interface{}的可能產生的類型進行註冊。具體就看一下下面這個例子:

package main

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"log"
)

type P struct {
	X, Y, Z int
	Name    interface{}
}

type Q struct {
	X, Y *int32
	Name interface{}
}

type Inner struct {
	Test int
}

func main() {
	var network bytes.Buffer        // Stand-in for a network connection
	enc := gob.NewEncoder(&network) // Will write to network.
	dec := gob.NewDecoder(&network) // Will read from network.
	
	gob.Register(Inner{})
	
	// Encode (send) the value.
	inner := Inner{1}
	err := enc.Encode(P{1,2,3, inner})
	if err != nil {
		log.Fatal("encode error:", err)
	}
	// Decode (receive) the value.
	var q Q
	err = dec.Decode(&q)
	if err != nil {
		log.Fatal("decode error:", err)
	}
	fmt.Println(q)
	fmt.Printf("%q: {%d,%d}\n", q.Name, *q.X, *q.Y)


}

這裏使用了gob.Register(Inner{})告訴系統:全部的Interface是有可能爲Inner結構的。

在這個例子中,若是你註釋了gob.Register, 系統會報錯。

RegisterName是和Register同樣的效果,只是在Register的同時也爲這個類型附上一個別名。

 

GebEncoder和GobDecoder

這是兩個接口,若是你的數據結構實現了這兩個接口,當調用encoder.Encode和decoder.Decode的時候就會調用這兩個結構的對應函數

看一下下面這個例子:

package main

import (
	"bytes"
	"encoding/gob"
	"fmt"
	"log"
)

type P struct {
	X, Y, Z int
	Name    string
}

func (this *P)GobEncode() ([]byte, error) {
    return []byte{},nil 
}

type Q struct {
	X, Y *int32
	Name string
}

func main() {
	var network bytes.Buffer        
	enc := gob.NewEncoder(&network) 
	dec := gob.NewDecoder(&network) 
	// Encode (send) the value.
	err := enc.Encode(P{3, 4, 5, "Pythagoras"})
	if err != nil {
		log.Fatal("encode error:", err)
	}
	// Decode (receive) the value.
	var q Q
	err = dec.Decode(&q)
	if err != nil {
		log.Fatal("decode error:", err)
	}
	fmt.Println(q)
	fmt.Printf("%q: {%d,%d}\n", q.Name, *q.X, *q.Y)

}

這裏個人P實現了GobEncoder接口,所以在enc.Encode的時候會調用func (this *P)GobEncode() ([]byte, error)

固然我這個函數直接返回的是空byte,所以在解碼的時候會報錯:decode error:gob: type mismatch in decoder: want struct type main.Q; got non-struct

這兩個接口暴露出來就表明你爲本身定義的結構進行編解碼規則制定。固然,若是使用本身的編解碼規則,在編碼和解碼的過程就須要是一對的

後記

gob包是golang提供的「私有」的編解碼方式,文檔中也說了它的效率會比json,xml等更高(雖然我也沒有驗證)。所以在兩個Go 服務之間的相互通訊建議不要再使用json傳遞了,徹底能夠直接使用gob來進行數據傳遞。

參考資料

http://blog.golang.org/2011/03/gobs-of-data.html

http://www.mikespook.com/2011/03/%E3%80%90%E7%BF%BB%E8%AF%91%E3%80%91gob-%E7%9A%84%E6%95%B0%E6%8D%AE/

相關文章
相關標籤/搜索