最近在做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文件,进而得到的。代码如下:
 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;
}

cinema.proto

 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();
}
}

Main.java

执行结果:

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.FileDescriptorSet: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中的应用 包含例子程序的更多相关文章

  1. java中请给出例子程序:找出两个数的最大公约数和最小公倍数

    9.2 找出12和8的最大公约数和最小公倍数.     public class Test {     public static void main(String[] args) {         ...

  2. java中请给出例子程序:找出n到m之间的质数。

    9.1 找出100到200之间的质数.  public class Test {     public static void main(String[] args){         for (in ...

  3. Protobuf动态解析那些事儿

    需求背景 在接收到 protobuf 数据之后,如何自动创建具体的 Protobuf Message 对象,再做反序列化.“自动”的意思主要有两个方面:(1)当程序中新增一个 protobuf Mes ...

  4. Android Java中的一些使用例子

    connectivity= ConnectivityService.getInstance(context); ServiceManager.addService(Context.CONNECTIVI ...

  5. .NET及JAVA 中如何使用代码启动程序

    .NET 中: System.Diagnostics.Process.Start("应用程序");    JAVA中: ProcessBuilder pb=new ProcessB ...

  6. java 中一个char包含几个字节

    背景   char包含几个字节可能记得在上学的时候书上写的是2个字节,一直没有深究,今天我们来探究一下到底一个char多少个字节? Char   char在设计之初的时候被用来存储字符,可是世界上有那 ...

  7. Java中死锁的简单例子及其避免

    死锁:当一个线程永远地持有一个锁,并且其他线程都尝试获得这个锁时,那么它们将永远被阻塞.比如,线程1已经持有了A锁并想要获得B锁的同时,线程2持有B锁并尝试获取A锁,那么这两个线程将永远地等待下去. ...

  8. java中TCP两个例子大写服务器和文件上传

    大写服务器的实例: package com.core.net; import java.io.BufferedReader; import java.io.BufferedWriter; import ...

  9. java 中解析xml的技术

    最初,XML 语言仅仅是意图用来作为 HTML 语言的替代品而出现的,但是随着该语言的不断发展和完善,人们越来越发现它所具有的优点:例如标记语言可扩展,严格的语法规定,可使用有意义的标记,内容存储和表 ...

随机推荐

  1. .net 利用Emit将object转为DbParameter,DataTable转为List<>

    先放测试结果图,测试的方法是拷贝了老赵的一个简单的性能计数器:CodeTimer.发现速度还是比利用反射来获取快了2倍左右的,将object转为DbParameter的反射方法我没写.         ...

  2. Hadoop的编译

    Hadoop2.4.0  重新编译 64  位本地库 原创作者:大鹏鸟 时间:2014-07-28 环境:虚拟机 VirtualBox,操作系统 64 位 CentOS 6.4 下载重新编译需要的软件 ...

  3. Xamarin开发笔记—百度在线语音合成

    语音合成:也被称为文本转换技术(TTS),它是将计算机自己产生的.或外部输入的文字信息转变为可以听得懂的.流利的口语输出的技术. 技术选型:语音合成初步选择有两个,一是讯飞.二是百度. 因为使用的是X ...

  4. 推荐xamlspy

    xamlspy(http://xamlspy.com/) 如果在win32时代用过spy++的,都应该在silverlight/wpf时代用一下xamlspy,让你重新找到用spy++看别人程序的UI ...

  5. Linux 安装配置 Tomcat

    1.下载 tomcat Linux 版本 oracle 官网下载地址:http://tomcat.apache.org/download-80.cgi 百度云盘链接:http://pan.baidu. ...

  6. oracle 11g 完全卸载方法

    网上好多卸载教程都前篇一律,但很多卸完重装都有问题,卸了几次装了几次,就特地总结整理一下 另外说一句:在完全删除(或者叫卸载)oracle时,没有必要特别意oracle提示问题,只要把oracle痕迹 ...

  7. ZooKeeper数据结构

    Time in ZooKeeper ZooKeeper跟踪时间的多种方式 1)Zxid:每个ZooKeeper状态变化将会接收到一个zxid(ZooKeeper Transaction Id)的时间戳 ...

  8. 用PHP和Ajax进行前后台数据交互——以用户登录为例

    很多网站中都有用户登录系统,要完成用户的注册和登陆,就一定要用到前后台的数据交互.在这里以简单的用户注册和登陆为例介绍一下前后台交互的大致流程. 首先,我们来做一个简单的登陆界面. 这里为了方便我使用 ...

  9. IDEA报错处理:Application Server was not connected before run configuration stop, reason: Unable to ping server at localhost:8080

    把wildfly的整个软件包更换成新的,配置文件重新配置,JBOSS_HOME环境变量修改成新的,在wildfly-10.1.0.FinalForTest\modules\system\layers\ ...

  10. android网页分享到朋友圈问题求助?

    目前想要实现通过QQ将网页分享到微信好友或朋友圈,看见有些APP是直接分享出去左下角图标显示的是QQ.求助各位大牛提供下思路. 这种功能是怎么实现的.应该不是通过android的系统分享实现的吧?.查 ...