最近在做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中的应用 包含例子程序就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
# Protobuf动态解析
# Java
# 在java程序中使用protobuf
# java程序中protobuf的基本用法示例
# 浅谈序列化之protobuf与avro对比(Java)
# 在Mac下IDEA安装并使用protobuf方式(Java)
# 序列化
# 多个
# 给大家
# 的是
# 这是
# 也有
# 看了
# 基础上
# 而来
# 希望能
# 相关资料
# 这篇
# 可通过
# 则可
# 较少
# 但不
# 都比
# 方法来
# 所有内容
# 小编
相关文章:
css网站制作参考文献有哪些,易聊怎么注册?
php json中文编码为null的解决办法
如何在服务器上配置二级域名建站?
网站图片在线制作软件,怎么在图片上做链接?
网站制作中优化长尾关键字挖掘的技巧,建一个视频网站需要多少钱?
建站之星如何快速生成多端适配网站?
贸易公司网站制作流程,出口贸易网站设计怎么做?
建站之家VIP精选网站模板与SEO优化教程整合指南
北京网站制作公司哪家好一点,北京租房网站有哪些?
如何通过商城免费建站系统源码自定义网站主题?
,南京靠谱的征婚网站?
建站之星好吗?新手能否轻松上手建站?
Android使用GridView实现日历的简单功能
制作网站的模板软件,网站怎么建设?
高端网站建设与定制开发一站式解决方案 中企动力
招贴海报怎么做,什么是海报招贴?
宝塔建站助手安装配置与建站模板使用全流程解析
常州企业网站制作公司,全国继续教育网怎么登录?
如何配置支付宝与微信支付功能?
阿里云网站制作公司,阿里云快速搭建网站好用吗?
创业网站制作流程,创业网站可靠吗?
焦点电影公司作品,电影焦点结局是什么?
济南网站制作的价格,历城一职专官方网站?
如何快速查询网站的真实建站时间?
已有域名和空间如何搭建网站?
制作无缝贴图网站有哪些,3dmax无缝贴图怎么调?
极客网站有哪些,DoNews、36氪、爱范儿、虎嗅、雷锋网、极客公园这些互联网媒体网站有什么差异?
如何在Golang中使用replace替换模块_指定本地或远程路径
如何用低价快速搭建高质量网站?
台州网站建设制作公司,浙江手机无犯罪记录证明怎么开?
购物网站制作费用多少,开办网上购物网站,需要办理哪些手续?
早安海报制作网站推荐大全,企业早安海报怎么每天更换?
宝塔面板创建网站无法访问?如何快速排查修复?
详解一款开源免费的.NET文档操作组件DocX(.NET组件介绍之一)
制作销售网站教学视频,销售网站有哪些?
已有域名建站全流程解析:网站搭建步骤与建站工具选择
网站制作软件免费下载安装,有哪些免费下载的软件网站?
济南专业网站制作公司,济南信息工程学校怎么样?
微信h5制作网站有哪些,免费微信H5页面制作工具?
如何用PHP快速搭建高效网站?分步指南
建站10G流量真的够用吗?如何应对访问高峰?
郑州企业网站制作公司,郑州招聘网站有哪些?
盘锦网站制作公司,盘锦大洼有多少5G网站?
全景视频制作网站有哪些,全景图怎么做成网页?
如何在Golang中处理模块冲突_解决依赖版本不兼容问题
如何制作算命网站,怎么注册算命网站?
JS中使用new Date(str)创建时间对象不兼容firefox和ie的解决方法(两种)
个人摄影网站制作流程,摄影爱好者都去什么网站?
如何做静态网页,sublimetext3.0制作静态网页?
网站专业制作公司有哪些,做一个公司网站要多少钱?
*请认真填写需求信息,我们会在24小时内与您取得联系。