最近在做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. mongo查询系统

    首先,我们先向集合(collections)中添加测试文档(documents).如下: > for(i=1;i<=5;i++) db.test.insert({x:i,y:i*i,z:6 ...

  2. python中xrange用法分析

    本文实例讲述了python中xrange用法.分享给大家供大家参考.具体如下: 先来看如下示例: >>> x=xrange(0,8) >>> print x xra ...

  3. 【JAVAEE学习笔记】hibernate03:多表操作详解、级联、关系维护和练习:添加联系人

    一.一对多|多对一 1.关系表达 表中的表达 实体中的表达 orm元数据中表达 一对多 <!-- 集合,一对多关系,在配置文件中配置 --> <!-- name属性:集合属性名 co ...

  4. css代码初始化

    @charset "utf-8";/* 页面元素初始化和常用样式定义-start *//*======== 全局 ========*/body, div, dl, dt, dd, ...

  5. 【论文:麦克风阵列增强】Microphone Array Post-Filtering For Non-Stationary Noise Suppression

    作者:桂. 时间:2017-06-08  08:01:41 链接:http://www.cnblogs.com/xingshansi/p/6957027.html 原文链接:http://pan.ba ...

  6. PHP编译

    编译php-5.3.6的时候需要先打一个内存泄露的补丁,具体的操作如下 tar vzxf suhosin-0.9.32.1.tar.gz tar vjxf php-5.3.6.tar.bz2 cd p ...

  7. angularJS插入html及更换iframe的src

    html: ng-bind-html <div class="tabs_content" ng-bind-html="specialHtml">&l ...

  8. 【源码分享】mui实现简单的手机音乐播放器

    mui实现简单的手机音乐播放器 最近先来无事,我用mui写了一个可以跨页面控制的音乐播放器.主要功能有上一曲,下一曲,播放,暂停,感兴趣的可以继续看下去. 说的总是不实在,直接上源码,有兴趣的可以读下 ...

  9. 10分钟就能学会的.NET Core配置

    .NET Core为我们提供了一套用于配置的API,它为程序提供了运行时从文件.命令行参数.环境变量等读取配置的方法.配置都是键值对的形式,并且支持嵌套,.NET Core还内建了从配置反序列化为PO ...

  10. Swift 求余运算

    求余运算 求余运算(a % b)是计算b的多少倍刚刚好可以容入a,返回多出来的那部分(余数). 注意:求余运算(%)在其他语言也叫取模运算.然而严格说来,我们看该运算符对负数的操作结果,"求 ...