分享
  1. 首页
  2. 文章

Go Protobuf 资源的可读化

findingea · · 1998 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

工作上有大量协议采用 Google Protocol Buffer,关于 Protobuf 的简单介绍可以看 IBM 的《Google Protocol Buffer 的使用和原理》这篇介绍。简单来说,Protobuf 的优点是(相比 XML)更小、更快、更简单,同时可以向后兼容。缺点的话,对我日常工作影响比较大的就是可读性较差,因为 Protobuf 压缩的时候会做序列化,生成 pb 文件,这个文件是二进制的,无法做到 human readable。但在日常工作中,尤其是排查问题是,经常需要看资源文件内容是否正确、上下游服务收发包内容是否正确、伪造 pb 资源等等,这些内容都是 pb 的,需要经过转换才能读懂,由此就用 Go 写了利用 JSON 伪造 pb 资源和反序列化 pb 打印成人类可读的文本的两段程序。

JSON 转 pb

这个感觉起来是件很麻烦的事情,但是有了 jsonpb 这个库之后,事情就变得很简单了。

首先定义 user.proto 。

syntax = "proto3";
package user_info;
message UserInfo {
 message User {
 string username = 1;
 uint32 age = 2;
 string graduate = 3;
 }
 
 repeated User user_list = 1;
}

然后再转换生成 user.pb.go 文件。

protoc --go_out=. user.proto

编写 JSON 文件,注意 key 的名字需要遵循 user.pb.go 中的名字,例如:

type UserInfo struct {
 UserList []*UserInfo_User `protobuf:"bytes,1,rep,name=user_list,json=userList" json:"user_list,omitempty"`
}
type UserInfo_User struct {
 Username string `protobuf:"bytes,1,opt,name=username" json:"username,omitempty"`
 Age uint32 `protobuf:"varint,2,opt,name=age" json:"age,omitempty"`
 Graduate string `protobuf:"bytes,3,opt,name=graduate" json:"graduate,omitempty"`
}

user.pb.go 已经指定了一个 field 在 JSON 中的命名,直接按照这个编写 JSON 文件即可。

{
 "userList": [
 {
 "username": "lawrencelin",
 "age": 28,
 "graduate": "Tongji University"
 },
 {
 "username": "findingsea",
 "age": 28,
 "graduate": "Fudan University"
 }
 ]
}

编写主代码:

package main
import (
 "github.com/golang/protobuf/proto"
 "io/ioutil"
 "os"
 "fmt"
 "github.com/golang/protobuf/jsonpb"
 "user_proto"
)
func main() {
 jsonFilePath := "/home/lawrence/GoglandProjects/JsonToPbIntro/json/user_info.json"
 pbFilePath := "/home/lawrence/GoglandProjects/JsonToPbIntro/pb/user_info.pb"
 buf, err := ioutil.ReadFile(jsonFilePath)
 if err != nil {
 fmt.Println("Read file err: ", err)
 os.Exit(0)
 }
 userInfo := &user_info.UserInfo{}
 if err = jsonpb.UnmarshalString(string(buf), userInfo); err != nil {
 fmt.Println("jsonpb UnmarshalString fail: ", err)
 os.Exit(0)
 }
 fmt.Println("user info pb: ", userInfo.String())
 data, err := proto.Marshal(userInfo)
 if err != nil {
 fmt.Println("proto Marshal fail: ", err)
 os.Exit(0)
 }
 if err = ioutil.WriteFile(pbFilePath, data, os.ModePerm); err != nil {
 fmt.Println("Write file err: ", err)
 }
}

核心函数就是 UnmarshalString ,输入是 JSON 字符串,输出 Protobuf 对象。

func UnmarshalString(str string, pb proto.Message) error

运行一下 main.go,就生成好了 user_info.pb 文件,打印如下:

user info pb: user_list:<username:"lawrencelin" age:28 graduate:"Tongji University" > user_list:<username:"findingsea" age:28 graduate:"Fudan University" > 

打印 Protobuf 对象

这一边本来应该很简单的,因为 Protobuf 库就提供了字符串转换函数,像 C++ 版 Protobuf 直接提供了 DebugString() 方法,可以直接输出可读的打印字符串。但是 Go 里面,我直觉反应调用了一下 String() 方法,fmt.Println("user info pb: ", userInfo.String()),发现只能打印成一行。

user_list:<username:"lawrencelin" age:28 graduate:"Tongji University" > user_list:<username:"findingsea" age:28 graduate:"Fudan University" > 

看了一下 String() 方法的实现,直接调用了 CompactTextString 方法:

func (m *UserInfo) String() string { return proto.CompactTextString(m) }
// CompactText writes a given protocol buffer in compact text format (one line).
func CompactText(w io.Writer, pb Message) error { return compactTextMarshaler.Marshal(w, pb) }
// CompactTextString is the same as CompactText, but returns the string directly.
func CompactTextString(pb Message) string { return compactTextMarshaler.Text(pb) }

注释里说明了这个接口只能返回压缩过的文本,这个可读性就很差了,那如何输出可读的 Protobuf 对象呢?

看了文档之后,发现应该使用 MarshalTextString 接口,就可以直接返回可读的文本格式 Protobuf 对象。其接口源码和注释如下:

// MarshalText writes a given protocol buffer in text format.
// The only errors returned are from w.
func MarshalText(w io.Writer, pb Message) error { return defaultTextMarshaler.Marshal(w, pb) }
// MarshalTextString is the same as MarshalText, but returns the string directly.
func MarshalTextString(pb Message) string { return defaultTextMarshaler.Text(pb) }

调用的方法很简单,fmt.Println(proto.MarshalTextString(userInfo)),输出:

user_list: <
 username: "lawrencelin"
 age: 28
 graduate: "Tongji University"
>
user_list: <
 username: "findingsea"
 age: 28
 graduate: "Fudan University"
>

有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:findingea

查看原文:Go Protobuf 资源的可读化

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1998 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏