Go Protobuf 资源的可读化

工作上有大量协议采用 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"
>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,100评论 18 139
  • Golang是我最喜欢的一门语言,它简洁、高效、易学习、开发效率高、还可以编译成机器码… 虽然它一出世,就饱受关注...
    盘木阅读 3,487评论 0 7
  • 我们都听过一万小时定律,就是通过一万小时的练习成为一方领域的专家,所用时间大约是十年,我们现在想学的技能太...
    兔妈妈1503阅读 2,019评论 8 8
  • 天喜自由 但笑的大方 哭的磊落 生性孤僻 却落落不群 侠骨柔情 单枪匹马也能所向披靡 在权势面前不减狂骄 在高贵面...
    于沛龙阅读 134评论 0 0