• 技术文章 >后端开发 >Golang

    golang序列化方法有哪些

    青灯夜游青灯夜游2023-01-04 19:33:28原创203

    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}

    个人认为这个例子是真的好。我们看到,结构体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”://m.sbmmt.com/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等),也可以是仅包含固定大小值的数组或结构体。详情可参考://m.sbmmt.com/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中文网其它相关文章!

    声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn核实处理。
    专题推荐:序列化 go语言 Golang
    上一篇:字节跳动需要用go语言吗 下一篇:自己动手写 PHP MVC 框架(40节精讲/巨细/新人进阶必看)

    相关文章推荐

    • golang中什么是微服务• go语言栈与堆都是什么• go语言中方法和函数的区别是什么• go语言结构体是什么意思• go语言可以做渗透测试吗
    1/1

    PHP中文网