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 := 0; i < 10; i++ {
        p := &Student{
            Name: fmt.Sprintf("name%d", i),
            Age:  rand.Intn(100),
            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, 0755)
    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元素里。
  • - 如果一个字段的标签为"a>b>c",则元素c将会嵌套进其上层元素a和b中。如果该字段相邻的字段标签指定了同样的上层元素,则会放在同一个XML元素里。

下面举个例子:

例如我想创建一个如下的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 := 0; i < 2; 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, 0755)
    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 = 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() 执行序列化

func writeProto(filename string) (err error) {
    //创建学生信息
    var students []*school.Student
    for i := 0; i < 30; i++ {

        var sex = (school.Sex)(i % 3)
        student := &school.Student{
            Name: fmt.Sprintf("Student_%d", i),
            Age:  15,
            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, 0755)
    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 不一致造成的

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。如果你想了解更多相关内容请查看下面相关链接

(0)

相关推荐

  • Goland中Protobuf的安装、配置和使用

    目录 引言 1.安装protobuf编译器 2.下载protobuf的golang支持库,安装protoc-gen-go 3.protobuf使用示例 引言 本文记录了mac环境下protobuf的编译安装,并通过一个示例来演示proto自动生成go代码. 本文使用的mac os 12.3系统,不建议使用homebrew安装,系统版本太高,会安装报错,所以自己下载新版压缩包编译构建安装. 1.安装protobuf编译器 在 官方github选择适合自己系统的Proto编译器程序进行下载安装 本文

  • 自定义Go Json的序列化方法译文

    编译自 Custom JSON Marshalling in Go. 我们知道,通过tag,可以有条件地实现定制Go JSON序列化的方式,比如json:",omitempty", 当字段的值为空的时候,我们可以在序列化后的数据中不包含这个值,而json:"-"可以直接不被JSON序列化,如果想被序列化key-,可以设置tag为json:"-,",加个逗号. 如果你为类型实现了MarshalJSON() ([]byte, error)和Unmar

  • 在go语言中安装与使用protobuf的方法详解

    简介 本文主要给大家介绍了关于go语言安装使用protobuf的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. protobuf是Google开发出来的一个语言无关.平台无关的数据序列化工具,在rpc或tcp通信等很多场景都可以使用.通俗来讲,如果客户端和服务端使用的是不同的语言,那么在服务端定义一个数据结构,通过protobuf转化为字节流,再传送到客户端解码,就可以得到对应的数据结构.这就是protobuf神奇的地方.并且,它的通信效率极高,"一条消息数据,用pr

  • Golang如何将日志以Json格式输出到Kafka

    目录 格式化接口 普通文本格式化器 Json文本格式化器 写日志接口 写日志到文件 写日志到Kafka 接口的组装 如何提高日志处理的吞吐量 在上一篇文章中我实现了一个支持Debug.Info.Error等多个级别的日志库,并将日志写到了磁盘文件中,代码比较简单,适合练手.有兴趣的可以通过这个链接前往:https://github.com/bosima/ylog/releases/tag/v1.0.1 工程实践中,我们往往还需要对日志进行采集,将日志归集到一起,然后用于各种处理分析,比如生产环境

  • golang 微服务之gRPC与Protobuf的使用

    RPC是什么? 所谓RPC(remote procedure call 远程过程调用)框架实际是提供了一套机制,使得应用程序之间可以进行通信,而且也遵从server/client模型.使用的时候客户端调用server端提供的接口就像是调用本地的函数一样. gRPC是什么? 与许多RPC系统一样,gRPC基于定义服务的思想,指定可以使用其参数和返回类型远程调用的方法.默认情况下,gRPC使用协议缓冲区作为接口定义语言(IDL)来描述服务接口和有效负载消息的结构. gRPC有什么好处以及在什么场景下

  • golang中json操作的完全指南

    目录 前言 1. 结构体与JSON互转 2. map与JSON互转 3. 结构体的变量不加tag标签能否正常转成json数据 4. JSON操作的一些小技巧 (1)忽略掉 struct 指定字段 (2)添加额外的字段 (3)合并两个 struct (4)字符串传递给 int类型 (5)一个 json 分成两个struct 补充:GoLang json格式化输出 总结 前言 JSON是一种轻量级的数据交换格式.易于阅读和编写. golang 提供了 encoding/json 包来操作JSON数据

  • go面向对象方式操作JSON库实现四则运算

    目录 前言 面向对象的方式操作 JSON 实现原理 对 JSON 做四则运算 总结 前言 在之前实现的 JSON 解析器中当时只实现了将一个 JSON 字符串转换为一个 JSONObject,并没有将其映射为一个具体的 struct:如果想要获取值就需要先做断言将其转换为 map 或者是切片再来获,会比较麻烦. decode, err := xjson.Decode(`{"glossary":{"title":"example glossary"

  • golang解析xml的方法

    本文实例讲述了golang解析xml的方法.分享给大家供大家参考,具体如下: golang解析xml真是好用,特别是struct属性的tag让程序简单了许多,其他变成语言需要特殊类型的在golang里直接使用tag舒服 xml文件点击此处本站下载. 完整示例代码: 复制代码 代码如下: package main import (     "os"     "encoding/xml"     // "encoding/json"     &quo

  • Go语言中的数据格式(json、xml 、msgpack、protobuf)使用总结

    在分布式的系统中,因为涉及到数据的传输,所以一定会进行数据的交换,此时就要定义数据交换的格式,例如二进制.Json.Xml等等.本篇文章就是总结一下常用的几种数据格式. 一.Json格式 如果想使用Json数据格式,可以借助于encoding/json这个包. 利用json包里的 json.Marshal(xxx) 和 json.Unmarshal(data, &xxx) 进行序列化和反序列化. 下面举个例子: package main import ( "encoding/json&q

  • 在PHP语言中使用JSON和将json还原成数组的方法

    在之前我写过php返回json数据简单实例,刚刚上网,突然发现一篇文章,也是介绍json的,还挺详细,值得参考.内容如下 从5.2版本开始,PHP原生提供json_encode()和json_decode()函数,前者用于编码,后者用于解码. 一.json_encode() <?php $arr = array ('a'=>1,'b'=>2,'c'=>3,'d'=>4,'e'=>5); echo json_encode($arr); ?> 输出 {"a&

  • 在Go语言中使用JSON的方法

    Encode 将一个对象编码成JSON数据,接受一个interface{}对象,返回[]byte和error: func Marshal(v interface{}) ([]byte, error) Marshal函数将会递归遍历整个对象,依次按成员类型对这个对象进行编码,类型转换规则如下: bool类型 转换为JSON的Boolean 整数,浮点数等数值类型 转换为JSON的Number string 转换为JSON的字符串(带""引号) struct 转换为JSON的Object,

  • 详解Go语言中结构体与JSON间的转换

    目录 前言 结构体转 JSON JSON 解析结构体 小结 前言 在日常开发中,我们往往会将 JSON 解析成对应的结构体,反之也会将结构体转成 JSON.接下来本文会通过 JSON 包的两个函数,来介绍 JSON 与结构体之间的转换. 结构体转 JSON Marshal(v any) ([]byte, error):将 v 转成 JSON 数据,以 []byte 的形式返回. import ( "encoding/json" "fmt" ) type User s

  • C#中DataTable 转换为 Json的方法汇总(三种方法)

    在web开发中,我们可能会有这样的需求,为了便于前台的JS的处理,我们需要将查询出的数据源格式比如:List<T>.DataTable转换为Json格式.特别在使用Extjs框架的时候,Ajax异步请求的数据格式就是Json.鉴于此,我今天来分享将DataTable 转换成 Json的3种方法.换句话说如何在ASP.NET将一个DataTable序列化为 Json数组.或者如何从一个DataTable返回一个Json字符串.这篇文章将采用StringBuilder,JavaScriptSeri

  • 详谈Java中net.sf.json包关于JSON与对象互转的坑

    在Web开发过程中离不开数据的交互,这就需要规定交互数据的相关格式,以便数据在客户端与服务器之间进行传递.数据的格式通常有2种:1.xml:2.JSON.通常来说都是使用JSON来传递数据.本文正是介绍在Java中JSON与对象之间互相转换时遇到的几个问题以及相关的建议. 首先明确对于JSON有两个概念: JSON对象(JavaScript Object Notation,JavaScript对象表示法).这看似只存是位JavaScript所定制的,但它作为一种语法是独立于语言以及平台的.只是说

  • Python中字典和JSON互转操作实例

    JSON是一种轻量级的数据交换格式,各种语言都有良好的支持.字典是Python的一种数据结构.可以看成关联数组. 有些时候我们需要设计到字典转换成JSON序列化到文件,或者从文件中读取JSON.简单备忘一下. Dict转JSON写入文件 复制代码 代码如下: #!/usr/bin/env python # coding=utf-8 import json d = {'first': 'One', 'second':2} json.dump(d, open('/tmp/result.txt', '

  • 浅谈Go语言中的结构体struct & 接口Interface & 反射

    结构体struct struct 用来自定义复杂数据结构,可以包含多个字段(属性),可以嵌套: go中的struct类型理解为类,可以定义方法,和函数定义有些许区别: struct类型是值类型. struct定义 type User struct { Name string Age int32 mess string } var user User var user1 *User = &User{} var user2 *User = new(User) struct使用 下面示例中user1和

  • JS中Eval解析JSON字符串的一个小问题

    之前写过一篇 关于 JSON 的介绍文章,里面谈到了 JSON 的解析.我们都知道,高级浏览器可以用 JSON.parse() API 将一个 JSON 字符串解析成 JSON 数据,稍微欠妥点的做法,我们可以用eval() 函数. JSON (JavaScript Object Notation)一种简单的数据格式,比xml更轻巧. JSON 是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON 数据不需要任何特殊的 API 或工具包. JSON的规则很简单:

  • PHP中SERIALIZE和JSON的序列化与反序列化操作区别分析

    本文实例讲述了PHP中SERIALIZE和JSON的序列化与反序列化操作区别.分享给大家供大家参考,具体如下: PHP中SERIALIZE和JSON序列化与反序列化区别是什么呢,对于这个问题我们可以和小编一起来看看,具体的操作细节如下所示. 在PHP中,serialize和json两种方式对一个对象或数组进行序列化或反序列化有什么区别呢? 假设一个对象和一个数组: $web = new stdClass; $web->site = 'tantengvip'; $web->owner = 'tu

随机推荐