当前位置: 移动技术网 > IT编程>开发语言>Java > 基于Protobuf动态解析在Java中的应用 包含例子程序

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

2019年07月22日  | 移动技术网IT编程  | 我要评论

最近在做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文件,进而得到的。

代码如下:

 cinema.proto

option java_package="com.liulei.cinema";

enum movietype{
 children=1;
 adult=2;
 normal=3;
 ohter=4;
}

enum gender{
 man=1;
 woman=2;
 other=3;
}

message movie{
 required string name=1;
 required movietype type=2;
 optional int32 releasetimestamp=3;
 optional string description=4;
}

message customer{
 required string name=1;
 optional gender gender=2;
 optional int32 birthdaytimestamp=3;
}

message ticket{
 required int32 id=1;
 required movie movie=2;
 required customer customer=3;
}

main.java

public static void main( string[] args ) {

  cinema.movie.builder moviebuilder = cinema.movie.newbuilder();
  moviebuilder.setname("the shining");
  moviebuilder.settype(cinema.movietype.adult);
  moviebuilder.setreleasetimestamp(327859200);

  system.out.println("dynamic message parse by proto file");
  try {
   byte[] buffer3 = new byte[moviebuilder.build().getserializedsize()];
   codedoutputstream codedoutputstream3 = codedoutputstream.newinstance(buffer3);
   try {
    moviebuilder.build().writeto(codedoutputstream3);
    system.out.println(buffer3);
   } catch (ioexception e) {
    e.printstacktrace();
   }
   string protoccmd = "protoc --descriptor_set_out=cinema.description ./cinema.proto --proto_path=.";
   process process = runtime.getruntime().exec(protoccmd);
   process.waitfor();
   int exitvalue = process.exitvalue();
   if (exitvalue != 0) {
    system.out.println("protoc execute failed");
    return;
   }
   descriptors.descriptor pbdescritpor = null;
   descriptorprotos.filedescriptorset descriptorset = descriptorprotos.filedescriptorset.parsefrom(new fileinputstream("./cinema.description"));
   for (descriptorprotos.filedescriptorproto fdp : descriptorset.getfilelist()) {
    descriptors.filedescriptor filedescriptor = descriptors.filedescriptor.buildfrom(fdp, new descriptors.filedescriptor[]{});
    for (descriptors.descriptor descriptor : filedescriptor.getmessagetypes()) {
     if (descriptor.getname().equals("movie")) {
      system.out.println("movie descriptor found");
      pbdescritpor = descriptor;
      break;
     }
    }
   }
   if (pbdescritpor == null) {
    system.out.println("no matched descriptor");
    return;
   }
   dynamicmessage.builder pbbuilder = dynamicmessage.newbuilder(pbdescritpor);

   message pbmessage = pbbuilder.mergefrom(buffer3).build();
   system.out.println(pbmessage);

  } catch (exception e) {
   system.out.println("exception");
   e.printstacktrace();
  }
 }

执行结果:

dynamic message parse from byte array
[b@597ccf6e
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动态解析在java中的应用 包含例子程序就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网