基于Protobuf动态解析在Java中的应用 包含例子程序
最近在做ProtoBuf相关的项目,其中用到了动态解析,网上看了下相关资料和博文都比较少,自己来写一个记录一下学习过程。
ProtocolBuffers是结构化数据格式标准,提供序列化和反序列方法,用于存储和交换。语言中立,平台无关、可扩展。目前官方提供了C++、Java、PythonAPI,也有其他语言的开源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
optionjava_package="com.liulei.cinema";
enumMovieType{
CHILDREN=1;
ADULT=2;
NORMAL=3;
OHTER=4;
}
enumGender{
MAN=1;
WOMAN=2;
OTHER=3;
}
messageMovie{
requiredstringname=1;
requiredMovieTypetype=2;
optionalint32releaseTimeStamp=3;
optionalstringdescription=4;
}
messageCustomer{
requiredstringname=1;
optionalGendergender=2;
optionalint32birthdayTimeStamp=3;
}
messageTicket{
requiredint32id=1;
requiredMoviemovie=2;
requiredCustomercustomer=3;
}
Main.java
publicstaticvoidmain(String[]args){
Cinema.Movie.BuildermovieBuilder=Cinema.Movie.newBuilder();
movieBuilder.setName("TheShining");
movieBuilder.setType(Cinema.MovieType.ADULT);
movieBuilder.setReleaseTimeStamp(327859200);
System.out.println("DynamicMessageParsebyprotofile");
try{
byte[]buffer3=newbyte[movieBuilder.build().getSerializedSize()];
CodedOutputStreamcodedOutputStream3=CodedOutputStream.newInstance(buffer3);
try{
movieBuilder.build().writeTo(codedOutputStream3);
System.out.println(buffer3);
}catch(IOExceptione){
e.printStackTrace();
}
StringprotocCMD="protoc--descriptor_set_out=cinema.description./cinema.proto--proto_path=.";
Processprocess=Runtime.getRuntime().exec(protocCMD);
process.waitFor();
intexitValue=process.exitValue();
if(exitValue!=0){
System.out.println("protocexecutefailed");
return;
}
Descriptors.DescriptorpbDescritpor=null;
DescriptorProtos.FileDescriptorSetdescriptorSet=DescriptorProtos.FileDescriptorSet.parseFrom(newFileInputStream("./cinema.description"));
for(DescriptorProtos.FileDescriptorProtofdp:descriptorSet.getFileList()){
Descriptors.FileDescriptorfileDescriptor=Descriptors.FileDescriptor.buildFrom(fdp,newDescriptors.FileDescriptor[]{});
for(Descriptors.Descriptordescriptor:fileDescriptor.getMessageTypes()){
if(descriptor.getName().equals("Movie")){
System.out.println("Moviedescriptorfound");
pbDescritpor=descriptor;
break;
}
}
}
if(pbDescritpor==null){
System.out.println("Nomatcheddescriptor");
return;
}
DynamicMessage.BuilderpbBuilder=DynamicMessage.newBuilder(pbDescritpor);
MessagepbMessage=pbBuilder.mergeFrom(buffer3).build();
System.out.println(pbMessage);
}catch(Exceptione){
System.out.println("Exception");
e.printStackTrace();
}
}
执行结果:
DynamicMessageParseFrombytearray
[B@597ccf6e
Moviedescriptorfound
name:"TheShining"
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
DescriptorProtos.Descriptor:描述一个message类型,通过getName()得到message的类名
以上这篇基于Protobuf动态解析在Java中的应用包含例子程序就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。