Go语言中的数据格式(json、xml 、msgpack、protobuf)
在分布式的系统中,因为涉及到数据的传输,所以一定会进行数据的交换,此时就要定义数据交换的格式,例如二进制、Json、Xml等等。本篇文章就是总结一下常用的几种数据格式。
一、Json格式
如果想使用Json数据格式,可以借助于encoding/json这个包。
利用json包里的 json.Marshal(xxx) 和 json.Unmarshal(data, &xxx) 进行序列化和反序列化。
下面举个例子:
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 := ; i < ; i++ {
p := &Student{
Name: fmt.Sprintf("name%d", i),
Age: rand.Intn(),
Sex: "Man",
} students = append(students, p)
} //执行序列化操作
data, err := json.Marshal(students)
if err != nil {
fmt.Printf("=marshal failed, err:%v\n", err)
return
} //将数据写到一个文件当中
err = ioutil.WriteFile(filename, data, )
if err != nil {
fmt.Printf("write file failed, err:%v\n", err)
return
} return
} //读取json数据
func readJson(filename string) (err error) {
var students []*Student
data, err := ioutil.ReadFile(filename)
if err != nil {
return
} err = json.Unmarshal(data, &students)
if err != nil {
return
} for _, v := range students {
fmt.Printf("%#v\n", v)
}
return
}
执行:
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
}
}
执行结果:
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元素里。
原文链接:https://blog.csdn.net/yuyinghua0302/article/details/84568531
下面举个例子:
例如我想创建一个如下的xml数据:
<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>
我就可以创建下面这样的结构体:
//最外层的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"`
}
写文件方法:
func writeXml(fileName string) (err error) {
//创建一个*Server类型的数组
var serverList []*Server
for i := ; i < ; i++ {
s := &Server{
ServerName: fmt.Sprintf("Server%d", i),
ServerIP: fmt.Sprintf("192.168.1.%d", i),
}
serverList = append(serverList, s)
} var myServers *Servers = &Servers{
Version: "2.0",
Servers: serverList,
} //执行序列化操作
data, err := xml.MarshalIndent(myServers, "", " ")
if err != nil {
fmt.Printf("=marshal failed, err:%v\n", err)
return
} //将数据写到一个文件当中
err = ioutil.WriteFile(fileName, data, )
if err != nil {
fmt.Printf("write file failed, err:%v\n", err)
return
} return
}
如上代码,使用了MarshalIndent方法,第一个参数是需要序列化的数据,第二参数是前缀,第三个是缩进的字符串(这里是四个空格),然后在main方法中调用一下即可(代码略)。
这里主要想说明一下结构体里面的标签:
XmlName可以省略不写,不写的话最外层就是用的结构体的名称,例如第一个结构体是Servers,那么xml最外层的节点名称就是Servers。
读的话,使用 xml.Unmarshal(data, &xxx) 就可以实现了。
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
}
三、msgPack格式
上面两种Json和Xml格式,都是文本格式的数据,好处在于能够方便的阅读。但是问题在于占用空间比较大。所以又出现了MsgPack这种格式,它是在json基础上转换为二进制进行传输的。对应关系像下面这个图:
MsgPack并没有官方的包,我们需要使用一个第三方的包,项目地址:https://github.com/vmihailenco/msgpack
实现比较简单,将 json.Marshal 和 json.Unmarshal 中的【 json】替换为【 maspack】即可,下面是对上面代码的改造,创建了10000个学生的数据。
四、protobuf格式
protobuf是Google公司开发出的一种数据格式。官方文档地址:https://developers.google.cn/protocol-buffers/ 。
简单讲它使用了IDL语言作为中间语言来串联不同的编程语言。不同的语言可以根据生成的IDL中间语言,生成自己的语言。
这样做有什么好处? 举个例子:当我们在协作开发的时候,A部门使用的是Go语言、B部分使用的是Java语言,C部门使用的是C#语言,当他们之间进行数据交换的时候,都要各自维护自己的结构体,才能进行数据的
序列化和反序列化,使用protobuf的好处就是只需要一个IDL描述,然后生成不同的语言的结构,这样维护一份就可以了。
同时 prototbuf的性能也很好,这也是它的一个优势。IDL语言使用的变长编码(根据整数的范围 0-255 那么这个数字就占用1个字节 ,如果使用定长编码的话 一个整数可能就是 4个字节)所以它的空间利用率是很好的。
那开发流程是怎样的?
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语言的插件
执行命令:go get -u github.com/golang/protobuf/protoc-gen-go
C. 创建一个简单的proto文件
//指定版本
//注意proto3与proto2的写法有些不同
syntax = "proto3"; //包名,通过protoc生成时go文件时
package school; //性别
//枚举类型第一个字段必须为0
enum Sex {
male = ;
female = ;
other =;
} //学生
message Student {
Sex sex = ;
string Name = ;
int32 Age =;
} //班级
message Class{
repeated Student Students =;
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() 执行序列化
func writeProto(filename string) (err error) {
//创建学生信息
var students []*school.Student
for i := ; i < ; i++ { var sex = (school.Sex)(i % )
student := &school.Student{
Name: fmt.Sprintf("Student_%d", i),
Age: ,
Sex: sex,
} students = append(students, student)
} //创建班级信息
var myClass school.Class
myClass.Name = "我的班级"
myClass.Students = students data, err := proto.Marshal(&myClass)
if err != nil {
fmt.Printf("marshal proto buf failed, err:%v\n", err)
return
} err = ioutil.WriteFile(filename, data, )
if err != nil {
fmt.Printf("write file failed, err:%v\n", err)
return
}
return
}
②使用proto.Unmarshal(data, &mySchool)执行反序列化
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
}
Q&A
如果在使用protobuf生成的Go文件,出现了如下的异常:
undefined: proto.ProtoPackageIsVersion3
这个时候可能是由于上面两步下载的protoc.exe 和 protobuf 的版本不一致导致的。
1. 可以清空下gopath下的 github.com\golang\protobuf 然后重新下载,并在github.com\golang\protobuf\protoc-gen-go 执行 go install 命令。
2. 检查一下是不是使用了 godep 等包管理工具,里面引用的版本和protoc.exe 不一致造成的
Go语言中的数据格式(json、xml 、msgpack、protobuf)的更多相关文章
- Swift语言中如何使用JSON数据教程
这是一篇翻译文章,原文出处:http://www.raywenderlich.com/82706/working-with-json-in-swift-tutorial Swift语言中如何使用JSO ...
- C#中另辟蹊径解决JSON / XML互转的问题
C#中另辟蹊径解决JSON / XML互转的问题 最近在一个POC的项目中要用到JSON和XML的相互转换, 虽然我知道很多类库如JSON.NET具备这种功能, 但是我还是另辟蹊径的使用Spider ...
- Go语言中各种数据格式转换
Go语言各种数据类型格式转换 package main import ( "encoding/json" "fmt" "reflect" & ...
- ASP.NET WebServce项目下添加Http服务,支持Get,Post请求方式;传输格式json/xml
由于WEBServce老项目中需要增添新的接口,而且添加的接口不希望被其它项目以引用Servces方式使用. 那么得在现有Service项目中添加Http请求方式来实现系统间数据交互.只需要告知请求地 ...
- PHP语言中使用JSON
原文地址:http://www.ruanyifeng.com/blog/2011/01/json_in_php.html 在PHP语言中使用JSON 目前,JSON已经成为最流行的数据交换格式之一,各 ...
- JSON在各种语言中的运用
1. JSON数据格式 JSON(JavaScript Object Notation) 从结构上看,所有的数据最终都可以分成三种类型: 第一种类型是scalar(标量),也就是一个单独的strin ...
- SpringMVC 中整合之JSON、XML
每次看到好的博客我就想好好的整理起来,便于后面自己复习,同时也共享给网络上的伙伴们! 博客地址: springMVC整合Jaxb2.xStream: http://www.cnblogs.com/h ...
- 在 Swift 语言中更好的处理 JSON 数据:SwiftyJSON
SwiftyJSON能够让在Swift语言中更加简便处理JSON数据. With SwiftyJSON all you have to do is: ? 1 2 3 4 let json = JSON ...
- (转)使用 CJSON 在C语言中进行 JSON 的创建和解析的实例讲解
使用 CJSON 在C语言中进行 JSON 的创建和解析的实例讲解 本文用代码简单介绍cjson的使用方法,1)创建json,从json中获取数据.2)创建json数组和解析json数组 1. 创 ...
随机推荐
- centOS安装配置NFS
环境 nfs 192.168.56.101 client 192.168.56.102 一.yum 安装 yum -y install nfs-utils rpcbind 192.168.56.101 ...
- 将自己的项目作为jar包发布到maven中央仓库
maven版本是3.5.0,jdk是1.8(注意,不是说项目是1.8就行,必须是环境变量里的也是,不能超过1.8,否则一大堆问题,执行mvn前用javac -version看下版本) 一:先在sona ...
- java当中请给出一个oracle的helloworld例子
[学习笔记] 2.oracle的helloworld例子: import java.sql.*;public class OracleHello{ public static void main ...
- Zuul【限流】
在项目中,大部分都会使用到hyrtrix做熔断机制,通过某个预定的阈值来对异常流量进行降级处理,除了做服务降级以外,还可以对服务进行限流,分流,排队等. 当然,zuul也能做到限流策略,最简单的方式就 ...
- 通过tushare获取股票价格
# Author llll # coding=utf-8 # ---描述# 完成股票 价格查询和展示# 不直接根据网页进行爬虫获取股票价格,而是通过已有组件查询股票价格,并保存到csv文件或者exce ...
- python 之 re模块、hashlib模块
6.16 re模块 正则就是用一些具有特殊含义的符号组合到一起(称为正则表达式)来描述字符或者字符串的方法.或者说:正则就是用来描述一类事物的规则.(在Python中)它内嵌在Python中,并通过 ...
- 小程序--e.target和e.currentTarget区别
事件捕获与事件冒泡 事件捕获是从外到内,事件冒泡是从内到外. 注意:不管是不是冒泡事件,都不会改变事件传递的参数值,都还是在dataset中获取(******) target:指事件源组件对象 ...
- PAT(B) 1062 最简分数(Java)
题目链接:1062 最简分数 (20 point(s)) 题目描述 一个分数一般写成两个整数相除的形式:N/M,其中 M 不为0.最简分数是指分子和分母没有公约数的分数表示形式. 现给定两个不相等的正 ...
- 2019牛客暑期多校训练营(第四场)A meeting(dfs或dp,dp待更新)
示例1: 输入: 4 21 23 13 42 4 输出:2 说明: They can meet at place 1 or 3. 题意:从K个点到达不联通图某个点需要的最短时间,这个最短时间是这K个人 ...
- Android--自定义Dialog style
<style name="dialog" parent="@android:style/Theme.Dialog"> <item name=& ...