Golang Gob编码

简介:

gob是Golang包自带的一个数据结构序列化的编码/解码工具。编码使用Encoder,解码使用Decoder。一种典型的应用场景就是RPC(remote procedure calls)。

gob和json的pack之类的方法一样,由发送端使用Encoder对数据结构进行编码。在接收端收到消息之后,接收端使用Decoder将序列化的数据变化成本地变量。

 

有一点需要注意,

发送方的结构和接受方的结构并不需要完全一致

结构体中缺省的字段将不会被发送。而且在接收方,并不需要所有的字段都要有对应的结构属性对应。godoc中的这个例子很形象:

clip_image001

当发送方传递的是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提供的函数

clip_image002

 

Encode和Decode

对于Encoder和Decoder可以看这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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,即

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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{}的可能产生的类型进行注册。具体就看一下下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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的时候就会调用这两个结构的对应函数

看一下下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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来进行数据传递。

目录
相关文章
|
Go
golang - gob与rpc
  今天和大家聊聊golang中怎么使用rpc,rpc数据传输会涉及到gob编码,所以先讲讲gob,别担心,就算你完全没有接触过gob与rpc,只要知道rpc的中文是远程过程调用,剩下的我都能给你讲明白(带你入门不包你精通)! 一、数据结构编码之gob   gob全称为:Go binary   Golang自带的一个数据结构序列化编码/解码工具,也就是说gob可以讲go中的一个数据结构序列化成某种东西,还能反序列化!序列化成啥我们后面来看,不管是变成一个字符串,变成二进制流,变成啥先不管,反正作用就是序列化。
2054 0
|
10天前
|
监控 算法 Go
Golang深入浅出之-Go语言中的服务熔断、降级与限流策略
【5月更文挑战第4天】本文探讨了分布式系统中保障稳定性的重要策略:服务熔断、降级和限流。服务熔断通过快速失败和暂停故障服务调用来保护系统;服务降级在压力大时提供有限功能以保持整体可用性;限流控制访问频率,防止过载。文中列举了常见问题、解决方案,并提供了Go语言实现示例。合理应用这些策略能增强系统韧性和可用性。
41 0
|
1天前
|
数据采集 人工智能 搜索推荐
快速入门:利用Go语言下载Amazon商品信息的步骤详解
本文探讨了使用Go语言和代理IP技术构建高效Amazon商品信息爬虫的方法。Go语言因其简洁语法、快速编译、并发支持和丰富标准库成为理想的爬虫开发语言。文章介绍了电商网站的发展趋势,如个性化推荐、移动端优化和跨境电商。步骤包括设置代理IP、编写爬虫代码和实现多线程采集。提供的Go代码示例展示了如何配置代理、发送请求及使用goroutine进行多线程采集。注意需根据实际情况调整代理服务和商品URL。
快速入门:利用Go语言下载Amazon商品信息的步骤详解
|
3天前
|
存储 编译器 Go
Go语言学习12-数据的使用
【5月更文挑战第5天】本篇 Huazie 向大家介绍 Go 语言数据的使用,包含赋值语句、常量与变量、可比性与有序性
38 6
Go语言学习12-数据的使用
|
4天前
|
Java Go
一文带你速通go语言指针
Go语言指针入门指南:简述指针用于提升效率,通过地址操作变量。文章作者sharkChili是Java/CSDN专家,维护Java Guide项目。文中介绍指针声明、取值,展示如何通过指针修改变量值及在函数中的应用。通过实例解析如何使用指针优化函数,以实现对原变量的直接修改。作者还邀请读者加入交流群深入探讨,并鼓励关注其公众号“写代码的SharkChili”。
11 0
|
4天前
|
存储 缓存 Java
来聊聊go语言的hashMap
本文介绍了Go语言中的`map`与Java的不同设计思想。作者`sharkChili`是一名Java和Go开发者,同时也是CSDN博客专家及JavaGuide项目的维护者。文章探讨了Go语言`map`的数据结构,包括`count`、`buckets指针`和`bmap`,解释了键值对的存储方式,如何利用内存对齐优化空间使用,并展示了`map`的初始化、插入键值对以及查找数据的源码过程。此外,作者还分享了如何通过汇编查看`map`操作,并鼓励读者深入研究Go的哈希冲突解决和源码。最后,作者提供了一个交流群,供读者讨论相关话题。
15 0
|
5天前
|
Java Go
Go语言学习11-数据初始化
【5月更文挑战第3天】本篇带大家通过内建函数 new 和 make 了解Go语言的数据初始化过程
18 1
Go语言学习11-数据初始化
|
5天前
|
自然语言处理 安全 Java
速通Go语言编译过程
Go语言编译过程详解:从词法分析(生成token)到句法分析(构建语法树),再到语义分析(类型检查、推断、匹配及函数内联)、生成中间码(SSA)和汇编码。最后,通过链接生成可执行文件。作者sharkchili,CSDN Java博客专家,分享技术细节,邀请读者加入交流群。
23 2
|
6天前
|
Java Linux Go
一文带你速通Go语言基础语法
本文是关于Go语言的入门介绍,作者因其简洁高效的特性对Go语言情有独钟。文章首先概述了Go语言的优势,包括快速上手、并发编程简单、设计简洁且功能强大,以及丰富的标准库。接着,文章通过示例展示了如何编写和运行Go代码,包括声明包、导入包和输出语句。此外,还介绍了Go的语法基础,如变量类型(数字、字符串、布尔和复数)、变量赋值、类型转换和默认值。文章还涉及条件分支(if和switch)和循环结构(for)。最后,简要提到了Go函数的定义和多返回值特性,以及一些常见的Go命令。作者计划在后续文章中进一步探讨Go语言的其他方面。
12 0
|
7天前
|
JavaScript 前端开发 Go
Go语言的入门学习
【4月更文挑战第7天】Go语言,通常称为Golang,是由Google设计并开发的一种编程语言,它于2009年公开发布。Go的设计团队主要包括Robert Griesemer、Rob Pike和Ken Thompson,这三位都是计算机科学和软件工程领域的杰出人物。
14 1