gob是Golang包自帶的一個數據結構序列化的編碼/解碼工具。編碼使用Encoder,解碼使用Decoder。一種典型的應用場景就是RPC(remote procedure calls)。
gob和json的pack之類的方法一樣,由發送端使用Encoder對數據結構進行編碼。在接收端收到消息之后,接收端使用Decoder將序列化的數據變化成本地變量。
有一點需要注意,
發送方的結構和接受方的結構並不需要完全一致
結構體中缺省的字段將不會被發送。而且在接收方,並不需要所有的字段都要有對應的結構屬性對應。godoc中的這個例子很形象:
當發送方傳遞的是struct{A, B int}結構的值的時候,接收方可以允許前9種結構,但是后面4種結構確實不允許的。
個人覺得這種設定是很符合邏輯的:接收端只接受和發送數據“相似”的數據結構。允許模擬相似,但是不允許矛盾。
各個類型的編解碼規則
整型:分為sign int和usign int, 其中從上面例子也看到,int和uint是不能互相編解碼的。float和int也是不能互相編解碼的。
Struct,array,slice是可以被編碼的。但是function和channel是不能被編碼的。
bool類型是被當作uint來編碼的,0是false,1是true。
浮點類型的值都是被當作float64類型的值來編碼的
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提供的函數
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來進行數據傳遞。