Golang Gob编码(gob包的使用详解)

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

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

基本使用

package main
import (
	"bytes"
	"encoding/gob"
	"fmt"
)

type MsgData struct {
	X, Y, Z int
	Name string
}
var network bytes.Buffer //网络传递的数据载体
func main() {
	err := senMsg()
	if err!=nil {
		fmt.Println("编码错误")
		return
	}
	err = revMsg()
	if err!=nil {
		fmt.Println("解码错误")
		return
	}
}

func senMsg()error {
	fmt.Print("开始执行编码(发送端)")

	enc := gob.NewEncoder(&network)
	sendMsg:=MsgData{3, 4, 5, "jiangzhou"}
	fmt.Println("原始数据:",sendMsg)
	err := enc.Encode(&sendMsg)
	fmt.Println("传递的编码数据为:",network)
	return  err
}
func revMsg()error {
	var revData MsgData
	dec:=gob.NewDecoder(&network)
	err:= dec.Decode(&revData) //传递参数必须为 地址
	fmt.Println("解码之后的数据为:",revData)
	return err
}

Register和RegisterName

1、编码的数据中有空接口类型,传递时赋值的空接口为:基本类型(int、float、string)、切片时,可以不进行注册。

package main
import (
	"bytes"
	"encoding/gob"
	"fmt"
)

type MsgData struct {
	X, Y, Z int
	Name string
	Msg interface{}
}
var network bytes.Buffer //网络传递的数据载体
func main() {
	err := senMsg()
	if err!=nil {
		fmt.Println("编码错误")
		return
	}
	err = revMsg()
	if err!=nil {
		fmt.Println("解码错误")
		return
	}
}

func senMsg()error {
	fmt.Print("开始执行编码(发送端)")
	enc := gob.NewEncoder(&network)
	s:=make([]string,0)
	s=append(s, "hello")
	//sendMsg:=MsgData{3, 4, 5, "jiangzhou",Msg{10001,"hello"}}
	//sendMsg:=MsgData{3, 4, 5, "jiangzhou",66.66}
	sendMsg:=MsgData{3, 4, 5, "jiangzhou",s}
	fmt.Println("原始数据:",sendMsg)
	err := enc.Encode(&sendMsg)
	fmt.Println("传递的编码数据为:",network)
	return  err
}
func revMsg()error {
	var revData MsgData
	dec:=gob.NewDecoder(&network)
	err:= dec.Decode(&revData) //传递参数必须为 地址
	fmt.Println("解码之后的数据为:",revData)
	return err
}

编码的数据中有空接口类型,传递时赋值的空接口为:map、struct时,必须进行注册。

package main
import (
	"bytes"
	"encoding/gob"
	"fmt"
)

type MsgData struct {
	X, Y, Z int
	Name string
	Msg interface{}
}
var network bytes.Buffer //网络传递的数据载体
func main() {
	err := senMsg()
	if err!=nil {
		fmt.Println("编码错误")
		return
	}
	err = revMsg()
	if err!=nil {
		fmt.Println("解码错误")
		return
	}
}

func senMsg()error {
	fmt.Print("开始执行编码(发送端)")

	enc := gob.NewEncoder(&network)

   m:=make(map[int]string)
	m[10001]="hello"
	m[10002]="jiangzhou"
	sendMsg:=MsgData{3, 4, 5, "jiangzhou",m}
	fmt.Println("原始数据:",sendMsg)
	err := enc.Encode(&sendMsg)
	fmt.Println("传递的编码数据为:",network)
	return  err
}
func revMsg()error {
	var revData MsgData
	dec:=gob.NewDecoder(&network)
	err:= dec.Decode(&revData) //传递参数必须为 地址
	fmt.Println("解码之后的数据为:",revData)
	return err
}

Register和RegisterName解决的主要问题是:当编解码中有一个字段是interface{}(interface{}的赋值为map、结构体时)的时候需要对interface{}的可能产生的类型进行注册。

正确代码为:

interface{}的赋值为map时:

package main
import (
	"bytes"
	"encoding/gob"
	"fmt"
)

type MsgData struct {
	X, Y, Z int
	Name string
	Msg interface{}
}
var network bytes.Buffer //网络传递的数据载体
func main() {
	err := senMsg()
	if err!=nil {
		fmt.Println("编码错误")
		return
	}
	err = revMsg()
	if err!=nil {
		fmt.Println("解码错误")
		return
	}
}

func senMsg()error {
	fmt.Print("开始执行编码(发送端)")

	enc := gob.NewEncoder(&network)

   m:=make(map[int]string)
	m[10001]="hello"
	m[10002]="jiangzhou"
	gob.Register(map[int]string{}) //TODO:进行了注册
	sendMsg:=MsgData{3, 4, 5, "jiangzhou",m}
	fmt.Println("原始数据:",sendMsg)
	err := enc.Encode(&sendMsg)
	fmt.Println("传递的编码数据为:",network)
	return  err
}
func revMsg()error {
	var revData MsgData
	dec:=gob.NewDecoder(&network)
	err:= dec.Decode(&revData) //传递参数必须为 地址
	fmt.Println("解码之后的数据为:",revData)
	return err
}

interface{}的赋值为结构体时:

package main
import (
	"bytes"
	"encoding/gob"
	"fmt"
)

type MsgData struct {
	X, Y, Z int
	Name    string
	Msg     interface{}
}

var network bytes.Buffer //网络传递的数据载体
func main() {
	err := senMsg()
	if err != nil {
		fmt.Println("编码错误",err)
		return
	}
	err = revMsg()
	if err != nil {
		fmt.Println("解码错误")
		return
	}
}

type Msg struct {
	Id     int
	Detail string
}

func senMsg() error {
	fmt.Print("开始执行编码(发送端)")
	enc := gob.NewEncoder(&network)
	gob.Register(Msg{}) //TODO:进行了注册
	s:=Msg{10001,"hello jiangzhou"}
	sendMsg := MsgData{3, 4, 5, "jiangzhou", s}
	fmt.Println("原始数据:", sendMsg)
	err := enc.Encode(&sendMsg)
	fmt.Println("传递的编码数据为:", network)
	return err
}
func revMsg() error {
	var revData MsgData
	dec := gob.NewDecoder(&network)
	err := dec.Decode(&revData) //传递参数必须为 地址
	fmt.Println("解码之后的数据为:", revData)
	return err
}

注:特别注意:以上代码中的结构体Msg对应的成员变量名称首字母一定要大写,不然会出现:编码错误编码错误 gob: type main.Msg has no exported fields

这里使用了

gob.Register(Msg{})

告诉系统:所有的Interface是有可能为Msg结构的。

在这个例子中,如果你注释了gob.Register, 系统会报错。

RegisterName是和Register一样的效果,只是在Register的同时也为这个类型附上一个别名。

补充:GO语音gob包的系列化和反序列化使用和遇到的错误

encoding/gob包实现了高效的序列化,特别是数据结构较复杂的,结构体、数组和切片都被支持。

package main

import (
 "bytes"
 "encoding/gob"
 "fmt"
)
//定义一个结构体
type Person struct {
 Age int
 Name string
}

func main() {
 p1:=Person{
  Age:  18,
  Name: "贪吃的猪",
 }
 //序列化
 //这里是储存的buffer
 var bufferr bytes.Buffer
 PerEncod:=gob.NewEncoder(&bufferr) //1.创建一个编码器
 err:=PerEncod.Encode(&p1) //编码
 if err != nil {
  fmt.Println("编码器 解码错误",err)
  return
 }
 //现在buffer就是完成储存序列化的
 fmt.Printf("序列化:buf%x\n",bufferr)

 //创建一个空的结构体来接受
 p2 :=Person{}
 //反序列化
 PerDecod:=gob.NewDecoder(bytes.NewReader(bufferr.Bytes()))//创建一个反编码器
 err=PerDecod.Decode(&p2)
 if err != nil {
  fmt.Println("PerDecod.Decode err:",err)
  return
 }
 fmt.Println("反序列化:",p2)
 //fmt.Printf("反序列化数据:string",p2)
}

系列化和反系列化的常见的错误

如果是你的结构体的字段是小写开头 gob序列化你的结构体的时候会找不到字段

如果我把

type Person struct {
    Age int
    Name string
}

改成

type Person struct {
    age int
    name string
}

编码器 解码错误 gob: type main.Person has no exported fields

解决方法就是把字段开头变成大写

这个错误还有一种可能造成的 你定义的结构里面还有一个结构 2

这个结构2的字段全部都是小写开头

解决方法就是把字段开头变成大写

今天是2019年11月2日 11:32 我的一个改了半天的bug 终于解决

gob在编译的时候 如果你的这个结构体里面包含另一个结构体

但是另一个结构体的字段开头没有大写

gob编译的时候是不会报错,他会不要没有大写的字段,

你反序列化的时候会发现这个字段是nil 空值

我去你码的

今天是2019年11月4日,今天新的序列化bug出現了

我生成秘钥对然后对密钥对进行数据序列化然后储存在文件里面

然后错误提示,在, gob: type not registered for interface: elliptic.p256Curve

其实gob是可以序列化全部结构,但是它不能序列化interface接口

因为接口的大小是无法定义的

密钥对的中的公钥结构体里面一个字段elliptic.Curve 他是接口

我们把这个接口进行注册就行了

gob提供了一个函数可以进行注册

gob.Register(elliptic.P256())

要gob遇到这个接口的时候按照elliptic.P256格式进行编译

然后就解决了~

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。如有错误或未考虑完全的地方,望不吝赐教。

(0)

相关推荐

  • go语言net包rpc远程调用的使用示例

    rpc 包提供了一个方法来通过网络或者其他的I/O连接进入对象的外部方法. 一个server注册一个对象, 标记它成为可见对象类型名字的服务.注册后,对象的外部方法就可以远程调用了.一个server可以注册多个 不同类型的对象,但是却不可以注册多个相同类型的对象. 只有满足这些标准的方法才会被远程调用视为可见:其他的方法都会被忽略: - 方法是外部可见的. - 方法有两个参数,参数的类型都是外部可见的. - 方法的第二个参数是一个指针. - 方法有返回类型错误 一.基于http的RPC 服务端:

  • golang的序列化与反序列化的几种方式

    golang用来序列化的模块有很多,我们来介绍3个. json 首先登场的是json,这个几乎毋庸置疑. 序列化 package main import ( "encoding/json" "fmt" ) type Girl struct { Name string Age int Gender string Where string Is_married bool } func main() { g := Girl{"satori", 16,

  • golang中的net/rpc包使用概述(小结)

    RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样. 我的项目是采用基于Restful的微服务架构,随着微服务之间的沟通越来越频繁,消耗的系统资源越来越多,于是乎就希望可以改成用rpc来做内部的通讯,对外依然用Restful.于是就想到了golang标准库的rpc包和google的grpc. 这篇文章重点了解一下golang的rpc包. 介绍 golang的rpc支持三个级别的RPC:TCP.HTTP.JSONR

  • Golang 实现 RTP音视频传输示例详解

    目录 引言 RTP 数据包头部字段 Golang 的相关实现 结尾 引言 在 Coding 之前我们先来简单介绍一下 RTP(Real-time Transport Protocol), 正如它的名字所说,用于互联网的实时传输协议,通过 IP 网络传输音频和视频的网络协议. 由音视频传输工作小组开发,1996 年首次发布,并提出了以下使用设想. 简单的多播音频会议 使用 IP 的多播服务进行语音通信.通过某种分配机制,获取多播组地址和端口对.一个端口用于音频数据的,另一个用于控制(RTCP)包,

  • 对python中url参数编码与解码的实例详解

    一.简介 在python中url,对于中文等非ascii码字符,需要进行参数的编码与解码. 二.关键代码 1.url编码 对字符串编码用urllib.parse包下的quote(string, safe='/', encoding=None, errors=None)方法. 对json格式的参数名和值编码,用urllib.parse包下的 urlencode(query, doseq=False, safe='', encoding=None, errors=None, quote_via=qu

  • MySQL5.6的zip包安装教程详解

    之前我们都是后缀为.msi的文件,换言之就是傻瓜式安装,但是有些版本不容易控制安装路径,或者数据库编码格式,还有些会安装很多无用的服务,但是都没有后缀为.zip文件简单直接,说是在哪里,就在哪里. 1,首先在官网下载一个合适的mysql版本,有msi文件,也有zip文件的,先前是下载了两个一个是5.7.24但是里面缺少一个文件夹:data,还缺少文件:my-default.ini文件,后来下载了5.6.42的zip压缩包,里面就什么都有了 2,把你下载的zip解压到合适的文件夹下面,会发现没有m

  • Go语言中strings和strconv包示例代码详解

    前缀和后缀 HasPrefix判断字符串s是否以prefix开头: strings.HaxPrefix(s string, prefix string) bool 示例: package main import ( "fmt" "strings" ) func main() { pre := "Thi" str1 := "This is a Go program!" fmt.Println(strings.HasPrefix(

  • java编程小白进阶包的作用详解

    目录 步骤 1  工具包里面有很多个工具类 步骤 2  StringUtil 步骤 3  目前的情况 步骤 4  CLASSPATH 步骤 5  具体配置 步骤 6  package 步骤 7  验证 步骤 8  亲自做一遍 步骤 9  为什么现在用Editplus运行报错了? 步骤 10  说白了 步骤 11  包的作用 包的作用,1是为了防止类和方法的重名,2是为了管理众多的java类. 步骤 1  工具包里面有很多个工具类 之前讲了打印数据的方法:System.out.println,写这

  • Golang配置解析神器go viper使用详解

    目录 前言 viper简介 功能 viper配置优先级 安装viper 支持哪些文件格式 key大小写问题 使用指南 如何访问viper的功能 配置默认值 读取配置文件 写配置文件 WriteConfig SafeWriteConfig WriteConfigAs SafeWriteConfigAs 监听配置文件 从io.Reader读取配置 显示设置配置项 注册和使用别名 读取环境变量 与命令行参数搭配使用 pflag 扩展其他flag 远程key/value存储支持 访问配置 直接访问 序列

  • Golang实现快速求幂的方法详解

    今天讲个有趣的算法:如何快速求nm,其中n和m都是整数. 为方便起见,此处假设m>=0,对于m< 0的情况,求出n|m|后再取倒数即可. 另外此处暂不考虑结果越界的情况(超过 int64 范围). 当然不能用编程语言的内置函数,我们只能用加减乘除来实现. n的m次方的数学含义是:m个n相乘:n*n*n...*n,也就是说最简单的方式是执行 m 次乘法. 直接用乘法实现的问题是性能不高,其时间复杂度是 O(m),比如 329要执行29次乘法,而乘法运算是相对比较重的,我们看看能否采用什么方法将时

  • Golang基础教程之字符串string实例详解

    目录 1. string的定义 2.string不可变 3.使用string给另一个string赋值 4.string重新赋值 补充:字符串拼接 总结 1. string的定义 Golang中的string的定义在reflect包下的value.go中,定义如下: StringHeader 是字符串的运行时表示,其中包含了两个字段,分别是指向数据数组的指针和数组的长度. // StringHeader is the runtime representation of a string. // I

  • Golang 中的 unsafe.Pointer 和 uintptr详解

    目录 前言 uintptr unsafe.Pointer 使用姿势 常规类型互转 Pointer => uintptr 指针算数计算:Pointer => uintptr => Pointer reflect 包中从 uintptr => Ptr 实战案例 string vs []byte sync.Pool 前言 日常开发中经常看到大佬们用各种 unsafe.Pointer, uintptr 搞各种花活,作为小白一看到 unsafe 就发憷,不了解二者的区别和场景,自然心里没数.

  • Golang中的错误处理的示例详解

    目录 1.panic 2.包装错误 3.错误类型判断 4.错误值判断 1.panic 当我们执行panic的时候会结束下面的流程: package main import "fmt" func main() { fmt.Println("hello") panic("stop") fmt.Println("world") } 输出: go run 9.go hellopanic: stop 但是panic也是可以捕获的,我们可

随机推荐