首頁 > 後端開發 > Golang > 主體

golang序列化方法有哪些

青灯夜游
發布: 2023-01-04 19:33:28
原創
4019 人瀏覽過

golang序列化方法有:1、利用Gob套件管理gob流,gob是和型別綁定的,如果發現多了或少了,會依據順序填入或截斷。 2.利用json包,能實現RFC 7159中定義的JSON編碼和解碼;在序列化的過程中,如果結構體內的成員是小寫的,則會出現錯誤。 3.利用Binary包,能實現數字和位元組序列之間的簡單轉換以及varint的編碼和解碼。 4.利用protobuf協議。

golang序列化方法有哪些

本教學操作環境:windows7系統、GO 1.18版本、Dell G3電腦。

在程式設計過程中,我們總是要遇到這樣的問題,就是將我們的資料物件要在網路中傳輸或儲存到文件,這就需要對其編碼和解碼動作。

目前存在著許多編碼格式:json, XML, Gob, Google Protocol Buffer 等,在Go 語言中,如何對資料進行這樣的編碼和解碼呢?

序列化和反序列化定義

#序列化(Serialization)是將物件的狀態資訊轉換為可以儲存或傳輸的形式的過程。在序列化期間,物件將其目前狀態寫入到臨時或持久性儲存區。

反過來,把變數從從儲存區重新讀取,重新建立該對象,則為反序列化。

在Go語言中,encoding 套件就是專門來處理這類序列化的編碼和解碼的問題。

序列化方式–Gob

#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}
登入後複製

個人認為這個例子是真的好。我們看到,結構體PQ 是不同的,我們看到Q 少了一個 Z 變數。

但是,在解碼的時候,仍然能解析得出來,這說明,使用gob 時,是根據類型綁定的,如果發現多了或者少了,會依據順序填充或截斷。

接下來,我們詳情說說怎麼編碼吧:

1. 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)

2. Encode 編碼

之後,對需要編碼序列化的結構體進行編碼:

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}
登入後複製

3. Decode 解碼

最後,對其解碼的步驟為:

dec := gob.NewDecoder(&network) // Will read from network.
if err = dec.Decode(&q);err != nil {
	log.Fatal("decode error 2:", err)
}
登入後複製

序列化方式–json

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 [{ } { }]}
登入後複製

我們看到,小寫的部分將不會被序列化到,也就是說,會是空值。

這個雖然不會報錯,但很明顯,不是我們想要看到的結果。

報錯:gob: type xxx has no exported fields

我們來看一個會報錯的例子:

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

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序列化方法有哪些的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板