最近在做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. js 实现倒计时效果

    <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...

  2. 磁盘配额quota

    磁盘配额 1 启用磁盘配额 首先创建新的分区 /dev/sd5,并创建文件系统. [root@local ~]# mkfs.ext4 /dev/sda5 由于xfs 不磁盘配额能成功,这里使用ext4 ...

  3. vue入门须知

    1.vue基本结构 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> & ...

  4. C#从基于FTPS的FTP server下载数据 (FtpWebRequest 的使用)SSL 加密

    FTPS,亦或是FTPES, 是FTP协议的一种扩展,用于对TLS和SSL协议的支持. 本文讲述了如何从一个基于FTPS的Server中下载数据的实例.   任何地方,如有纰漏,欢迎诸位道友指教.   ...

  5. HTML5浏览器定位navigator.geolocation.getCurrentPosition

    <!DOCTYPE html> <html> <body> <p id="demo">点击这个按钮,获得您的坐标:</p> ...

  6. 流畅的python学习笔记:第一章

    这一章中作者简要的介绍了python数据模型,主要是python的一些特殊方法.比如__len__, __getitem__. 并用一个纸牌的程序来讲解了这些方法 首先介绍下Tuple和nametup ...

  7. 关于Javascript循环体变量声明与初始化的效率问题

    针对循环体变量声明与初始化的效率问题,将执行的简单测试代码如下: function test(n) { console.time('Internally initialized'); for (var ...

  8. JS采用ActiveXObject实现用户在提交表单时屏蔽敏感词的功能

    本例中敏感词ciku.txt放在C盘根目录下,采用的ActiveXObject插件获取本地文件内容.使用此插件不需网上下插件,直接用如下js代码即可. 浏览器需修改interner安全选项的级别,启用 ...

  9. .net 4.0 中的特性总结(三):垃圾回收

    1.内存基础知识 每个进程都有其自己单独的虚拟地址空间. 同一台计算机上的所有进程共享相同的物理内存,如果有页文件,则也共享页文件. 默认情况下,32 位计算机上的每个进程都具有 2 GB 的用户模式 ...

  10. iOS 实现简单的毛玻璃效果

    最近在整理导航栏的渐隐渐现效果,整理过程中偶然学会了图片的毛玻璃效果实现,很简单,不多说了,先上图看看效果对比, 这是原图, 这是加了效果后的,创建图片的代码就不上了,下面看下添加效果的代码: // ...