Go Protobuf 资源的可读化

栏目: Go · 发布时间: 7年前

内容简介:工作上有大量协议采用 Google Protocol Buffer,关于 Protobuf 的简单介绍可以看 IBM 的这个感觉起来是件很麻烦的事情,但是有了首先定义 user.proto 。

工作上有大量协议采用 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"
>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Machine Learning in Action

Machine Learning in Action

Peter Harrington / Manning Publications / 2012-4-19 / GBP 29.99

It's been said that data is the new "dirt"—the raw material from which and on which you build the structures of the modern world. And like dirt, data can seem like a limitless, undifferentiated mass. ......一起来看看 《Machine Learning in Action》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

URL 编码/解码
URL 编码/解码

URL 编码/解码