您好,登錄后才能下訂單哦!
最近在做ProtoBuf相關(guān)的項(xiàng)目,其中用到了動(dòng)態(tài)解析,網(wǎng)上看了下相關(guān)資料和博文都比較少,自己來寫一個(gè)記錄一下學(xué)習(xí)過程。
Protocol Buffers是結(jié)構(gòu)化數(shù)據(jù)格式標(biāo)準(zhǔn),提供序列化和反序列方法,用于存儲(chǔ)和交換。語言中立,平臺(tái)無關(guān)、可擴(kuò)展。目前官方提供了C++、Java、Python API,也有其他語言的開源api(比如php)??赏ㄟ^ .proto文件生成對(duì)應(yīng)語言的類代碼
如果已知protobuf內(nèi)容對(duì)應(yīng)的是哪個(gè)類對(duì)象,則可以直接使用反序列化方法搞定(Xxx.parseFrom(inputStream)由二進(jìn)制轉(zhuǎn)換,TextFormat.merge(string, xxxBuilder)由文本轉(zhuǎn)換)
而我們經(jīng)常遇到的情況是,拿到一個(gè)被protobuf序列化的二進(jìn)制內(nèi)容,但不知道它的類型,無法獲得對(duì)應(yīng)的類對(duì)象。這種多見于需要處理各種各樣未知的ProtoBuf對(duì)象的系統(tǒng)。ProtoBuf提供了動(dòng)態(tài)解析機(jī)制來解決這個(gè)問題,它要求提供二進(jìn)制內(nèi)容的基礎(chǔ)上,再提供對(duì)應(yīng)類的Descriptor對(duì)象,在解析時(shí)通過DynamicMessage類的成員方法來獲得對(duì)象結(jié)果。
最后問題就是Descriptor對(duì)象從哪里來?這是通過protoc --descriptor_set_out=$outputpath 命令生成descriptor文件,進(jìn)而得到的。
代碼如下:
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(); } }
執(zhí)行結(jié)果:
Dynamic Message Parse From byte array
[B@597ccf6e
Movie descriptor found
name: "The Shining"
type: ADULT
releaseTimeStamp: 327859200
解釋具體過程:
0.首先對(duì).proto文件使用protoc命令,生成的descriptor文件中包含多個(gè)類對(duì)應(yīng)的descriptor類信息(序列化的DescriptorSet內(nèi)容)
1.首先取出序列化的DescriptorSet內(nèi)容,F(xiàn)ileDescriptorSet.parseFrom方法反序列化得到FileDescriptorSet對(duì)象
2.取出對(duì)應(yīng)message類型的Descriptor。
DescriptorSet成員方法getFileList(),拿到多個(gè)FileDescriptorProto對(duì)象,再構(gòu)建對(duì)應(yīng)FileDescriptor。
FileDescriptor的成員方法getMessageTypes()得到所有Message的Descriptor對(duì)象,找到對(duì)應(yīng)名字的Descriptor
3.用Descriptor對(duì)象反序列化對(duì)象
構(gòu)建DynamicMessage.Builder對(duì)象builder,再調(diào)用builder的mergeFrom/merge方法得到Message對(duì)象
其中Descriptor相關(guān)類:
DescriptorProtos.DescriptorSet:protoc編譯出來類文件中包含這個(gè)類,描述多個(gè).proto文件中的類
DescriptorProtos.FileDescriptorProto:描述一個(gè)完整的.proto文件中的類
DescriptorProtos.FileDescriptor:由DescriptorProtos.FileDescriptorProto構(gòu)建而來(buildFrom),描述1個(gè)完整.proto文件中的所有內(nèi)容,包括message類型的Descriptor和其他被導(dǎo)入文件的Descriptor。
getMessageTypes()方法:返回List<Descriptors.Descriptor>。得到FileDescriptor內(nèi),所有message類型直接兒子的Descriptor列表
DescriptorProtos.Descriptor:描述一個(gè)message類型,通過getName()得到message的類名
以上這篇基于Protobuf動(dòng)態(tài)解析在Java中的應(yīng)用 包含例子程序就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持億速云。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。