protobuf动态解析

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了protobuf动态解析相关的知识,希望对你有一定的参考价值。

参考技术A enum command



// 登录请求

CMD_LOGIN_RSP = 502 [(message_name) = "LoginRsp"];



extend google.protobuf.MessageOptions





// CMD_LOGIN_REQ = 501

message LoginReq



option (gateproto.protocolid) = CMD_LOGIN_REQ;

required string account = 1;

required string name = 2;

required bytes token = 3;

required uint64 roleid = 4;

required uint32 is_reconnect = 5[ default = 0];

optional uint32 imgid = 6[ default = 0];

optional string device_id = 7;



using namespace std;

using namespace google::protobuf;

using namespace google::protobuf::compiler;

using namespace google::protobuf::internal;

static DiskSourceTree srctree_;

static Importer *importer_;

static DynamicMessageFactory MsgFactory_;

struct MsgDesc



const FileDescriptor *filedesc;

const Descriptor *desc;

const Message *prototype;

;

std::map<string, MsgDesc *> descmap_;

std::vector< MsgDesc *> vecDesc_;

const FieldDescriptor * protocolid_des;

class FileErrorCollector : public MultiFileErrorCollector

public:

void AddError(const string& filename, int line, int column,

const string& message)



cout << filename << endl;

cout << line << endl;

cout << column << endl;

cout << message << endl;



;

void AddSearchPath(vector<string>& paths)



for (size_t i = 0; i < paths.size(); i++)



srctree_.MapPath("", paths[i]);



FileErrorCollector err;

importer_ = new Importer(&srctree_, &err);



void AddMsgDesc(const string& proto_file)



const FileDescriptor *file_desc = importer_->Import(proto_file);

int count = file_desc->message_type_count();

if (count <= 0)

return;

int extensiopn_couny = file_desc->extension_count();

printf("extensiopn_couny type: %d\n", extensiopn_couny);

for (int i = 0; i < extensiopn_couny; i++)



const FieldDescriptor * msg_desc = file_desc->extension(i);

printf("extension type: %s\n", msg_desc->name().c_str());

printf("extension cpp type: %s\n", msg_desc->cpp_type_name());

printf("extension type: %s\n", msg_desc->type_name());

if ( msg_desc->name() == "protocolid")



protocolid_des = msg_desc;





// string key = proto_file.substr(0, proto_file.find_last_of("."));

for (int i = 0; i < count; i++)



const Descriptor * msg_desc = file_desc->message_type(i);

printf("message type: %s\n", msg_desc->name().c_str());

MsgDesc *msgdesc = new MsgDesc;

msgdesc->filedesc = file_desc;

msgdesc->desc = msg_desc;

msgdesc->prototype = MsgFactory_.GetPrototype(msg_desc);

descmap_[msg_desc->name()] = msgdesc;

printf("message type: %s\n", msg_desc->options().DebugString().c_str());

/*bool m = msg_desc->options().GetExtension(gateproto::protocolid);

if (m)

printf("message type: %s\n", msg_desc->name().c_str());

vecDesc_.push_back(msgdesc);

*/





void SendGsRandomMessage()



uint32_t index = 10;

auto iter = descmap_.find("EnterPvePassReq");

if (iter!= descmap_.end() )

printf("SendGsRandomMessage");

MsgDesc *msgdesc = iter->second;

Message *msg = msgdesc->prototype->New();

printf("SendGsRandomMessage field count type: %d\n",msg->GetDescriptor()->field_count());

printf("SendGsRandomMessage extension count type: %d\n",msg->GetDescriptor()->extension_count());

printf("SendGsRandomMessage one count type: %d\n",msg->GetDescriptor()->oneof_decl_count());

printf("SendGsRandomMessage enum count type: %d\n",msg->GetDescriptor()->enum_type_count());

const Descriptor * option_Desc = msg->GetDescriptor()->options().GetDescriptor();

for (int i=0;i<option_Desc->field_count();i++)

const FieldDescriptor * filed_des = option_Desc->field(i);

printf("FieldDescriptor type: %s\n", filed_des->name().c_str());



for (int i=0;i<msg->GetDescriptor()->field_count();i++)

const FieldDescriptor * filed_des = msg->GetDescriptor()->field(i);

printf("FieldDescriptor type: %s\n", filed_des->name().c_str());



MessageOptions msgop= msg->GetDescriptor()->options();

const ::google::protobuf::UnknownFieldSet& unset = msgop.unknown_fields();

for (int i=0;i<unset.field_count();i++)

UnknownField filed = unset.field(i);

printf("UnknownField %d",filed.varint());







int main(int argc, char *argv[])



vector<string> v;

v.push_back("../protocol");

v.push_back("../include");

AddSearchPath(v);

AddMsgDesc("gate.pb/gate.proto");

AddMsgDesc("rank.pb/rank.proto");

AddMsgDesc("immortaldb.pb/immortaldb.proto");

AddMsgDesc("dbproxy.pb/dbproxy.proto");

AddMsgDesc("online.pb/online.proto");

AddMsgDesc("login.pb/login.proto");

SendGsRandomMessage();

return 0;


</pre>

Protobuf动态解析在Java中的应用 包含例子程序

最近在做ProtoBuf相关的项目,其中用到了动态解析,网上看了下相关资料和博文都比较少,自己来写一个记录一下学习过程。
 
Protocol Buffers是结构化数据格式标准,提供序列化和反序列方法,用于存储和交换。语言中立,平台无关、可扩展。目前官方提供了C++、Java、Python API,也有其他语言的开源api(比如php)。可通过 .proto文件生成对应语言的类代码
如果已知protobuf内容对应的是哪个类对象,则可以直接使用反序列化方法搞定(Xxx.parseFrom(inputStream)由二进制转换,TextFormat.merge(string, xxxBuilder)由文本转换)
 
而我们经常遇到的情况是,拿到一个被protobuf序列化的二进制内容,但不知道它的类型,无法获得对应的类对象。这种多见于需要处理各种各样未知的ProtoBuf对象的系统。ProtoBuf提供了动态解析机制来解决这个问题,它要求提供二进制内容的基础上,再提供对应类的Descriptor对象,在解析时通过DynamicMessage类的成员方法来获得对象结果。
最后问题就是Descriptor对象从哪里来?这是通过protoc --descriptor_set_out=$outputpath 命令生成descriptor文件,进而得到的。代码如下:
技术分享
 1 option java_package="com.liulei.cinema";
 2 
 3 enum MovieType{
 4     CHILDREN=1;
 5     ADULT=2;
 6     NORMAL=3;
 7     OHTER=4;
 8 }
 9 
10 enum Gender{
11     MAN=1;
12     WOMAN=2;
13     OTHER=3;
14 }
15 
16 message Movie{
17     required string name=1;
18     required MovieType type=2;
19     optional int32 releaseTimeStamp=3;
20     optional string description=4;
21 }
22 
23 message Customer{
24     required string name=1;
25     optional Gender gender=2;
26     optional int32 birthdayTimeStamp=3;
27 }
28 
29 message Ticket{
30     required int32 id=1;
31     required Movie movie=2;
32     required Customer customer=3;
33 }
cinema.proto
技术分享
 1 public static void main( String[] args ) {
 2 
 3         Cinema.Movie.Builder movieBuilder = Cinema.Movie.newBuilder();
 4         movieBuilder.setName("The Shining");
 5         movieBuilder.setType(Cinema.MovieType.ADULT);
 6         movieBuilder.setReleaseTimeStamp(327859200);
 7 
 8         System.out.println("Dynamic Message Parse by proto file");
 9         try {
10             byte[] buffer3 = new byte[movieBuilder.build().getSerializedSize()];
11             CodedOutputStream codedOutputStream3 = CodedOutputStream.newInstance(buffer3);
12             try {
13                 movieBuilder.build().writeTo(codedOutputStream3);
14                 System.out.println(buffer3);
15             } catch (IOException e) {
16                 e.printStackTrace();
17             }
18             String protocCMD = "protoc --descriptor_set_out=cinema.description ./cinema.proto --proto_path=.";
19             Process process = Runtime.getRuntime().exec(protocCMD);
20             process.waitFor();
21             int exitValue = process.exitValue();
22             if (exitValue != 0) {
23                 System.out.println("protoc execute failed");
24                 return;
25             }
26             Descriptors.Descriptor pbDescritpor = null;
27             DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(new FileInputStream("./cinema.description"));
28             for (DescriptorProtos.FileDescriptorProto fdp : descriptorSet.getFileList()) {
29                 Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fdp, new Descriptors.FileDescriptor[]{});
30                 for (Descriptors.Descriptor descriptor : fileDescriptor.getMessageTypes()) {
31                     if (descriptor.getName().equals("Movie")) {
32                         System.out.println("Movie descriptor found");
33                         pbDescritpor = descriptor;
34                         break;
35                     }
36                 }
37             }
38             if (pbDescritpor == null) {
39                 System.out.println("No matched descriptor");
40                 return;
41             }
42             DynamicMessage.Builder pbBuilder = DynamicMessage.newBuilder(pbDescritpor);
43 
44             Message pbMessage = pbBuilder.mergeFrom(buffer3).build();
45             System.out.println(pbMessage);
46 
47         } catch (Exception e) {
48             System.out.println("Exception");
49             e.printStackTrace();
50         }
51     }
Main.java
执行结果:

Dynamic Message Parse From byte array
[[email protected]
Movie descriptor found
name: "The Shining"
type: ADULT
releaseTimeStamp: 327859200

 
解释具体过程:
0.首先对.proto文件使用protoc命令,生成的descriptor文件中包含多个类对应的descriptor类信息(序列化的DescriptorSet内容)
1.首先取出序列化的DescriptorSet内容,FileDescriptorSet.parseFrom方法反序列化得到FileDescriptorSet对象
2.取出对应message类型的Descriptor。
     DescriptorSet成员方法getFileList(),拿到多个FileDescriptorProto对象,再构建对应FileDescriptor。
     FileDescriptor的成员方法getMessageTypes()得到所有Message的Descriptor对象,找到对应名字的Descriptor
3.用Descriptor对象反序列化对象
     构建DynamicMessage.Builder对象builder,再调用builder的mergeFrom/merge方法得到Message对象
 
其中Descriptor相关类:
DescriptorProtos.DescriptorSet:protoc编译出来类文件中包含这个类,描述多个.proto文件中的类
DescriptorProtos.FileDescriptorProto:描述一个完整的.proto文件中的类
DescriptorProtos.FileDescriptor:由DescriptorProtos.FileDescriptorProto构建而来(buildFrom),描述1个完整.proto文件中的所有内容,包括message类型的Descriptor和其他被导入文件的Descriptor。
      getMessageTypes()方法:返回List<Descriptors.Descriptor>。得到FileDescriptor内,所有message类型直接儿子的Descriptor列表    
DescriptorProtos.Descriptor:描述一个message类型,通过getName()得到message的类名
 
 转载请注明出处。欢迎拍砖~
 
 
 





以上是关于protobuf动态解析的主要内容,如果未能解决你的问题,请参考以下文章

golang中使用消息名称创建protobuf消息

# # # ProtoBuf

protobuf-net 中的动态 protobuf 消息

protobuf源码解析与netty+rpc实战

Protobuf 从入门到实战

Protobuf 从入门到实战