golang序列化方法有:1、利用Gob包管理gob流,gob是和型別繫結的,如果發現多了或者少了,會依據順序填充或者截斷。2、利用json包,能實現RFC 7159中定義的JSON編碼和解碼;在序列化的過程中,如果結構體內的成員是小寫的,則會出現錯誤。3、利用Binary包,能實現數位和位元組序列之間的簡單轉換以及varint的編碼和解碼。4、利用protobuf協定。
本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。
在程式設計過程中,我們總是要遇到這樣的問題,就是將我們的資料物件要在網路中傳輸或儲存到檔案,這就需要對其編碼和解碼動作。
目前存在很多編碼格式:json, XML, Gob, Google Protocol Buffer 等,在Go 語言中,如何對資料進行這樣的編碼和解碼呢?
序列化 (Serialization)是將物件的狀態資訊轉換為可以儲存或傳輸的形式的過程。在序列化期間,物件將其當前狀態寫入到臨時或永續性儲存區。
反過來,把變數從從儲存區中重新讀取,重新建立該物件,則為反序列化。
在Go語言中, 包就是專門來處理這類序列化的編碼和解碼的問題。
gob
包管理 gob 流–編碼器(傳送器)和解碼器(接收器)之間交換的二進位制值。一個典型的用途是傳輸遠端過程呼叫(RPCs)的引數和結果,如 "net/rpc "包中就使用了gobs 流。
具體可以參考檔案:https://docs.studygolang.com/pkg/encoding/gob/
他的官網給出了一個範例:
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
}
// This example shows the basic usage of the package: Create an encoder,
// transmit some values, receive them with a decoder.
func main() {
// Initialize the encoder and decoder. Normally enc and dec would be
// bound to network connections and the encoder and decoder would
// run in different processes.
var network bytes.Buffer // Stand-in for a network connection //Buffer是具有Read和Write方法的可變大小的位元組緩衝區。
enc := gob.NewEncoder(&network) // Will write to network.
dec := gob.NewDecoder(&network) // Will read from network.
// Encode (send) some values.
err := enc.Encode(P{3, 4, 5, "Pythagoras"})
if err != nil {
log.Fatal("encode error:", err)
}
err = enc.Encode(P{1782, 1841, 1922, "Treehouse"})
if err != nil {
log.Fatal("encode error:", err)
}
// Decode (receive) and print the values.
var q Q
err = dec.Decode(&q)
if err != nil {
log.Fatal("decode error 1:", err)
}
fmt.Printf("%q: {%d, %d}\n", q.Name, *q.X, *q.Y)
err = dec.Decode(&q)
if err != nil {
log.Fatal("decode error 2:", err)
}
fmt.Printf("%q: {%d, %d}\n", q.Name, *q.X, *q.Y)
}
登入後複製
執行結果是:
"Pythagoras": {3, 4}
"Treehouse": {1782, 1841}
登入後複製
個人認為這個例子是真的好。我們看到,結構體P
和 Q
是不同的,我們看到Q
少了一個 Z
變數。
但是,在解碼的時候,仍然能解析得出來,這說明,使用 gob
時,是根據型別繫結的,如果發現多了或者少了,會依據順序填充或者截斷。
接下來,我們詳情說說怎麼編碼吧:
bytes.Buffer
型別首先,我們需要定義一個 bytes.Buffer
型別,用來承接需要序列化的結構體,這個型別是這樣的:
// A Buffer is a variable-sized buffer of bytes with Read and Write methods.(Buffer是具有Read和Write方法的可變大小的位元組緩衝區)
// The zero value for Buffer is an empty buffer ready to use.
type Buffer struct {
buf []byte // contents are the bytes buf[off : len(buf)]
off int // read at &buf[off], write at &buf[len(buf)]
lastRead readOp // last read operation, so that Unread* can work correctly.
}
登入後複製
使用上面的例子,可以看到輸出是:
"Pythagoras": {3, 4} ==>
{[42 255 129 3 1 1 1 80 1 255 130 0 1 4 1 1 88 1 4 0 1 1 89 1 4 0 1 1 90 1 4 0 1 4 78 97 109 101 1 12 0 0 0 21 255 130 1 6 1 8 1 10 1 10 80 121 116 104 97 103 111 114 97 115 0] 0 0}
登入後複製
可以看到,Buffer
裡,是二進位制數(一個位元組8個bit,最高255)
之後,對需要編碼序列化的結構體進行編碼:
enc := gob.NewEncoder(&network) // Will write to network.
// Encode (send) some values.
if err := enc.Encode(P{3, 4, 5, "Pythagoras"}); err != nil {
log.Fatal("encode error:", err)
}
登入後複製
這裡,首先是要獲得 *Encoder
物件,獲得物件後,利用 *Encoder
物件的方法 Encode
進行編碼。
這裡,需要注意的是,
Encode
如果是網路程式設計的,其實是可以直接傳送訊息給對方的,而不必進行 socket 的send 操作。
比如:在 srever
端有程式碼:
func main() {
l, err := net.Listen("tcp", "127.0.0.1:8000") //監聽埠
if err != nil {
log.Fatal("net Listen() error is ", err)
}
p := P{
1, 2, 3,
"name"}
conn, err := l.Accept()
if err != nil {
log.Fatal("net Accept() error is ", err)
}
defer func() { _ = conn.Close() }()
//引數是conn 時,即可發出
enc := gob.NewEncoder(conn)
if err = enc.Encode(p); err != nil { //發生結構體資料
log.Fatal("enc Encode() error is ", err)
}
}
登入後複製
在使用者端client有:
func main() {
conn,err := net.Dial("tcp","127.0.0.1:8000")
if err != nil {
log.Fatal("net Dial() error is ", err)
}
defer func() { _ = conn.Close() }()
/**
type Q struct {
X, Y int
Name string
}
*/
var q Q
dec := gob.NewDecoder(conn)
if err = dec.Decode(&q); err != nil {
log.Fatal("enc Encode() error is ", err)
}
fmt.Println(q)
}
登入後複製
輸出:
{1 2 name}
登入後複製
最後,對其解碼的步驟為:
dec := gob.NewDecoder(&network) // Will read from network.
if err = dec.Decode(&q);err != nil {
log.Fatal("decode error 2:", err)
}
登入後複製
json
包實現了 RFC 7159
中定義的 JSON
編碼和解碼。JSON和Go值之間的對映在 Marshal 和 Unmarshal 函數的檔案中進行了描述。
有關此程式包的介紹,請參見「 JSON和Go」:https://www.php.cn/link/241200d15bc67211b50bd10815259e58json/
範例如下:
type Message struct {
QQ string
Address string
}
type Student struct {
Id uint64 `json:"id"` //可以保證json欄位按照規定的欄位跳脫,而不是輸出 Id
Age uint64 `json:"age"`
Data []Message
}
func main() {
m1 := Message{QQ: "123", Address: "beijing"}
m2 := Message{QQ: "456", Address: "beijing"}
s1 := Student{3, 19, append([]Message{}, m1, m2)}
var buf []byte
var err error
if buf, err = json.Marshal(s1); err != nil {
log.Fatal("json marshal error:", err)
}
fmt.Println(string(buf))
var s2 Student
if err = json.Unmarshal(buf, &s2); err != nil {
log.Fatal("json unmarshal error:", err)
}
fmt.Println(s2)
}
//輸出:
//{"id":3,"age":19,"Data":[{"QQ":"123","Address":"beijing"},{"QQ":"456","Address":"beijing"}]}
//{3 19 [{123 beijing} {456 beijing}]}
登入後複製
在序列化的過程中,如果結構體內的成員是小寫的,則會出現錯誤。以上兩種方式,都會出現這樣的結果
我們以 json
序列化為例子,看一下如果是小寫的話,會出現什麼樣的結果:
package main
import (
"encoding/json"
"fmt"
"log"
)
type Message struct {
qq string
address string
}
type Student struct {
Id uint64 `json:"id"` //可以保證json欄位按照規定的欄位跳脫,而不是輸出 Id
Age uint64 `json:"age"`
Data []Message
}
func main() {
m1 := Message{"123", "beijing"}
m2 := Message{"456", "beijing"}
s1 := Student{3, 19, append([]Message{}, m1, m2)}
var buf []byte
var err error
if buf, err = json.Marshal(s1); err != nil {
log.Fatal("json marshal error:", err)
}
fmt.Println(string(buf))
var s2 Student
if err = json.Unmarshal(buf, &s2); err != nil {
log.Fatal("json unmarshal error:", err)
}
fmt.Println(s2)
}
登入後複製
輸出:
{"id":3,"age":19,"Data":[{},{}]}
{3 19 [{ } { }]}
登入後複製
我們看到,小寫的部分將不會被序列化到,也就是說,會是空值。
這個雖然不會報錯,但是很明顯,不是我們想要看到的結果。
我們來看一個會報錯的例子:
type Message struct {
qq string
address string
}
type Student struct {
Id uint64 `json:"id"` //可以保證json欄位按照規定的欄位跳脫,而不是輸出 Id
Age uint64 `json:"age"`
Data []Message
}
func main() {
m1 := Message{"123", "beijing"}
m2 := Message{"456", "beijing"}
s1 := Student{3, 19, append([]Message{}, m1, m2)}
var buf bytes.Buffer
enc := gob.NewEncoder(&buf)
if err := enc.Encode(s1); err != nil {
log.Fatal("encode error:", err) //報錯
}
fmt.Println(string(buf.Bytes()))
}
登入後複製
這段程式碼會報錯:
2020/12/30 16:44:47 encode error:gob: type main.Message has no exported fields
登入後複製
提醒我們注意,結構體的大小寫是很敏感的!!!
Binary
包實現 數位 和 位元組 序列之間的簡單轉換以及varint的編碼和解碼。
通過讀取和寫入固定大小的值來轉換數位。 固定大小的值可以是固定大小的算術型別(bool,int8,uint8,int16,float32,complex64等),也可以是僅包含固定大小值的陣列或結構體。詳情可參考:https://www.php.cn/link/241200d15bc67211b50bd10815259e58binary/#Write
範例:
package main
import (
"bytes"
"encoding/binary"
"fmt"
)
func main() {
buf := new(bytes.Buffer)
var pi int64 = 255
err := binary.Write(buf, binary.LittleEndian, pi)
if err != nil {
fmt.Println("binary.Write failed:", err)
}
fmt.Println( buf.Bytes())
}
//輸出:
[255 0 0 0 0 0 0 0]
登入後複製
這裡需要注意:如果序列化的型別是 int
型別的話,將會報錯:
binary.Write failed: binary.Write: invalid type int
登入後複製
而且,序列化的值是空的。
這是由於,他在前面已經解釋清楚了,只能序列化固定大小的型別(bool,int8,uint8,int16,float32,complex64…),或者是結構體和固定大小的陣列。
當然,go語言還有其他的序列化方法,如 protobuf
協定,參考:https://geektutu.com/post/quick-go-protobuf.html
【相關推薦:Go視訊教學、】
以上就是golang序列化方法有哪些的詳細內容,更多請關注TW511.COM其它相關文章!