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语言中,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}个人认为这个例子是真的好。我们看到,结构体p 和 q 是不同的,我们看到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序列化方法有哪些的详细内容,更多请关注其它相关文章!
我和老王不太熟