Go语言中的数据格式(json、xml 、msgpack、protobuf)使用总结
来源:脚本之家
时间:2022-12-28 10:42:03 264浏览 收藏
本篇文章主要是结合我之前面试的各种经历和实战开发中遇到的问题解决经验整理的,希望这篇《Go语言中的数据格式(json、xml 、msgpack、protobuf)使用总结》对你有很大帮助!欢迎收藏,分享给更多的需要的朋友学习~
在分布式的系统中,因为涉及到数据的传输,所以一定会进行数据的交换,此时就要定义数据交换的格式,例如二进制、Json、Xml等等。本篇文章就是总结一下常用的几种数据格式。
一、Json格式
如果想使用Json数据格式,可以借助于encoding/json这个包。
利用json包里的 json.Marshal(xxx) 和 json.Unmarshal(data, &xxx) 进行序列化和反序列化。
下面举个例子:
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 | package main import ( "encoding/json" "fmt" "io/ioutil" "math/rand" ) type Student struct { Name string Age int Sex string } //写入json数据 func writeJson(filename string ) (err error ) { var students []*Student //随机生成10个学生数据 for i := 0 ; i <p>执行:</p> <pre class= "brush:go;" > func main() { filename := "C:/tmp/Students.txt" err := writeJson(filename) if err != nil { fmt.Printf( "write json failed, err:%v\n" , err) return } err = readJson(filename) if err != nil { fmt.Printf( "read json failed, err:%v\n" , err) return } }</pre> <p>执行结果:</p> |
- 1.可以看到在C:/tmp/下面生成了一个Students.txt文件,打开里面存放是刚刚随机生成的10个学生数据
- 2.执行结果可以看到控制台打印:
二、Xml格式
Xml格式也是我们常用的数据格式,同样要使用Xml格式,可以使用encoding/xml这个包。
像上面json一样,同样存在 xml.Marshal(xxx) 和 xml.Unmarshal(data, &xxx) 两个方法。此外还有方法xml.MarshalIndent(xxx) 可以格式化xml
先熟悉一下XML对应 标签怎么写:
- - XMLName字段,如上所述,会省略
- - 具有标签"-"的字段会省略
- - 具有标签"name,attr"的字段会成为该XML元素的名为name的属性
- - 具有标签",attr"的字段会成为该XML元素的名为字段名的属性
- - 具有标签",chardata"的字段会作为字符数据写入,而非XML元素
- - 具有标签",innerxml"的字段会原样写入,而不会经过正常的序列化过程
- - 具有标签",comment"的字段作为XML注释写入,而不经过正常的序列化过程,该字段内不能有"--"字符串
- - 标签中包含"omitempty"选项的字段如果为空值会省略
空值为false、0、nil指针、nil接口、长度为0的数组、切片、映射 - - 匿名字段(其标签无效)会被处理为其字段是外层结构体的字段
- - 如果一个字段的标签为"a>b>c",则元素c将会嵌套进其上层元素a和b中。如果该字段相邻的字段标签指定了同样的上层元素,则会放在同一个XML元素里。
- - 如果一个字段的标签为"a>b>c",则元素c将会嵌套进其上层元素a和b中。如果该字段相邻的字段标签指定了同样的上层元素,则会放在同一个XML元素里。
下面举个例子:
例如我想创建一个如下的xml数据:
1 | < servers version = "2.0" >< server >< servername >Server0</ servername >< serverip >192.168.1.0</ serverip ></ server >< server >< servername >Server1</ servername >< serverip >192.168.1.1</ serverip ></ server ></ servers > |
我就可以创建下面这样的结构体:
1 2 3 4 5 6 7 8 9 10 11 12 | //最外层的xml type Servers struct { XMLName xml.Name `xml: "Servers" ` Version string `xml: "version,attr" ` Servers []*Server `xml: "server" ` } //具体的server type Server struct { ServerName string `xml: "serverName" ` ServerIP string `xml: "serverIP" ` } |
写文件方法:
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 | func writeXml(fileName string ) (err error ) { //创建一个*Server类型的数组 var serverList []*Server for i := 0 ; i <p>如上代码,使用了MarshalIndent方法,第一个参数是需要序列化的数据,第二参数是前缀,第三个是缩进的字符串(这里是四个空格),然后在main方法中调用一下即可(代码略)。</p> <p>这里主要想说明一下结构体里面的标签:</p> <p>XmlName可以省略不写,不写的话最外层就是用的结构体的名称,例如第一个结构体是Servers,那么xml最外层的节点名称就是Servers。</p> <p>读的话,使用 xml.Unmarshal(data, &xxx) 就可以实现了。</p> <pre class= "brush:go;" > func readXml(fileName string ) (err error ) { var myServers *Servers data, err := ioutil.ReadFile(fileName) if err != nil { return } err = xml.Unmarshal(data, &myServers) if err != nil { return } fmt.Printf( "XMLNAME = %v\n" , myServers.XMLName) fmt.Printf( "Version = %v\n" , myServers.Version) for _, v := range myServers.Servers { fmt.Printf( "%v\n" , v) } return }</pre> <h2>三、msgPack格式</h2> <p>上面两种Json和Xml格式,都是文本格式的数据,好处在于能够方便的阅读。但是问题在于占用空间比较大。所以又出现了MsgPack这种格式,它是在json基础上转换为二进制进行传输的。对应关系像下面这个图:</p> <p style= "text-align:center" ><img alt= "" src= "/uploads/20221228/167219640463abb13431950.png" ></p> <p>MsgPack并没有官方的包,我们需要使用一个第三方的包,项目地址:<a target= "_blank" href= "https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq5roGCUgXuytMyero5ko5XFfIfNhNCyr5q5aWLEiZyjvpCko4qQbqLH3J-sl2Sb3L2Lf9aB3LGjgd9-m7yGgWiyfYqcf42Lo7LNq2uNn4Tes4iYmYequbOGmH2qr2SFZb6zkpp_inZ1" >https://github.com/vmihailenco/msgpack</a></p> <p>实现比较简单,将 json.Marshal 和 json.Unmarshal 中的【 json】替换为【 maspack】即可,下面是对上面代码的改造,创建了 10000 个学生的数据。</p> <p style= "text-align:center" ><img alt= "" src= "/uploads/20221228/167219640463abb134af0ee.png" ></p> <h2>四、protobuf格式</h2> <p>protobuf是Google公司开发出的一种数据格式。官方文档地址:<a target= "_blank" href= "https://www.17golang.com/gourl/?redirect=MDAwMDAwMDAwML57hpSHp6VpkrqbYLx2eayza4KafaOkbLS3zqSBrJvPsa5_0Ia6sWuR4Juaq6t9nq5roGCUgXuytMyero55jZe-i6nam6q6spvfZZ7EZGmix4CNpYlrba7Iq7CumHlwz8ZmqNiR0bqmktOGqcWraJ6usoWaeWuLpLOnza-CeYyasnuC3YW3yKKR4HllsXabaL-NhWOJs4ayv7e8ooKfhJo" >https://developers.google.cn/protocol-buffers/</a> 。</p> <p>简单讲它使用了IDL语言作为中间语言来串联不同的编程语言。不同的语言可以根据生成的IDL中间语言,生成自己的语言。</p> <p>这样做有什么好处? 举个例子:当我们在协作开发的时候,A部门使用的是 Go 语言、B部分使用的是Java语言,C部门使用的是C#语言,当他们之间进行数据交换的时候,都要各自维护自己的结构体,才能进行数据的</p> <p>序列化和反序列化,使用protobuf的好处就是只需要一个IDL描述,然后生成不同的语言的结构,这样维护一份就可以了。</p> <p>同时 prototbuf的性能也很好,这也是它的一个优势。IDL语言使用的变长编码(根据整数的范围 0 - 255 那么这个数字就占用 1 个字节 ,如果使用定长编码的话 一个整数可能就是 4 个字节)所以它的空间利用率是很好的。</p> <p>那开发流程是怎样的?</p> |
- A. IDL编写
- B. 生成只定语言的代码
- C. 序列化和反序列化
如何在Go中应用prototbuf
A.安装protoc编译器
解压后拷贝到GOPATH/bin目录下, 下载地址:https://github.com/google/protobuf/releases
然后把bin下面的protoc.exe 这个放到GoPath下的bin中,打开cmd,输入protoc,应该会出现如下内容:
如果不存在,可以将Gopath的bin加入到系统的环境变量path当中。
B.安装生成Go语言的插件
执行命令:
1 | go get -u github.com /golang/protobuf/protoc-gen-go |
C. 创建一个简单的proto文件
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 | //指定版本 //注意proto3与proto2的写法有些不同 syntax = "proto3" ; //包名,通过protoc生成时go文件时 package school; //性别 //枚举类型第一个字段必须为0 enum Sex { male = 0 ; female = 1 ; other = 2 ; } //学生 message Student { Sex sex = 1 ; string Name = 2 ; int32 Age = 3 ; } //班级 message Class{ repeated Student Students = 1 ; string Name; } |
message 就可以理解成类, repeated可以理解成数组。
D.利用之前下载好的protoc.exe 生成一个Go的代码。
第一个【.】代表当前输出的目录,后面*.proto则是 proto文件的路径
protoc--go_out=. *.proto
protoc --go_out=.\school\ .\school.proto
执行之后会生成如下的文件,这个go文件就可以直接使用了。
E. 使用生成的Go文件
①使用 proto.Marshal() 执行序列化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | func writeProto(filename string ) (err error ) { //创建学生信息 var students []*school.Student for i := 0 ; i <p>②使用proto.Unmarshal(data, &mySchool)执行反序列化</p> <pre class= "brush:go;" > func readProto(filename string ) (err error ) { var mySchool school.Class data, err := ioutil.ReadFile(filename) if err != nil { return } err = proto.Unmarshal(data, &mySchool) if err != nil { return } fmt.Printf( "proto:%v\n" , mySchool) return }</pre> <h2>Q&A</h2> <p>如果在使用protobuf生成的 Go 文件,出现了如下的异常:</p> <blockquote><p>undefined: proto.ProtoPackageIsVersion3</p></blockquote> <p>这个时候可能是由于上面两步下载的protoc.exe 和 protobuf 的版本不一致导致的。</p> |
- 1. 可以清空下gopath下的 github.com\golang\protobuf 然后重新下载,并在github.com\golang\protobuf\protoc-gen-go 执行 go install 命令。
- 2. 检查一下是不是使用了 godep 等包管理工具,里面引用的版本和protoc.exe 不一致造成的
今天关于《Go语言中的数据格式(json、xml 、msgpack、protobuf)使用总结》的内容就介绍到这里了,是不是学起来一目了然!想要了解更多关于golang的内容请关注golang学习网公众号!
-
138 收藏
-
443 收藏
-
171 收藏
-
329 收藏
-
377 收藏
-
119 收藏
-
381 收藏
-
362 收藏
-
255 收藏
-
332 收藏
-
239 收藏
-
402 收藏
-
119 收藏
-
180 收藏
-
230 收藏
-
184 收藏
-
168 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 508次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
-
- 俊逸的手机
- 这篇技术贴太及时了,太全面了,真优秀,收藏了,关注师傅了!希望师傅能多写Golang相关的文章。
- 2023-02-20 22:26:26
-
- 坚定的美女
- 写的不错,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢楼主分享技术贴!
- 2023-02-02 08:42:47
-
- 呆萌的百合
- 这篇技术贴真是及时雨啊,太详细了,赞 👍👍,码起来,关注作者了!希望作者能多写Golang相关的文章。
- 2022-12-31 14:34:02
-
- 爱笑的小蝴蝶
- 很好,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢作者分享文章!
- 2022-12-28 17:05:08