最近在做ProtoBuf相關的項目,其中用到了動態解析,網上看了下相關資料和博文都比較少,自己來寫一個記錄一下學習過程。 Protocol Buffers是結構化數據格式標準,提供序列化和反序列方法,用於存儲和交換。語言中立,平臺無關、可擴展。目前官方提供了C++、Java、Python API,也 ...
最近在做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文件,進而得到的。代碼如下:
1 option java_package="com.liulei.cinema"; 2 3 enum MovieType{ 4 CHILDREN=1; 5 ADULT=2; 6 NORMAL=3; 7 OHTER=4; 8 } 9 10 enum Gender{ 11 MAN=1; 12 WOMAN=2; 13 OTHER=3; 14 } 15 16 message Movie{ 17 required string name=1; 18 required MovieType type=2; 19 optional int32 releaseTimeStamp=3; 20 optional string description=4; 21 } 22 23 message Customer{ 24 required string name=1; 25 optional Gender gender=2; 26 optional int32 birthdayTimeStamp=3; 27 } 28 29 message Ticket{ 30 required int32 id=1; 31 required Movie movie=2; 32 required Customer customer=3; 33 }cinema.proto
1 public static void main( String[] args ) { 2 3 Cinema.Movie.Builder movieBuilder = Cinema.Movie.newBuilder(); 4 movieBuilder.setName("The Shining"); 5 movieBuilder.setType(Cinema.MovieType.ADULT); 6 movieBuilder.setReleaseTimeStamp(327859200); 7 8 System.out.println("Dynamic Message Parse by proto file"); 9 try { 10 byte[] buffer3 = new byte[movieBuilder.build().getSerializedSize()]; 11 CodedOutputStream codedOutputStream3 = CodedOutputStream.newInstance(buffer3); 12 try { 13 movieBuilder.build().writeTo(codedOutputStream3); 14 System.out.println(buffer3); 15 } catch (IOException e) { 16 e.printStackTrace(); 17 } 18 String protocCMD = "protoc --descriptor_set_out=cinema.description ./cinema.proto --proto_path=."; 19 Process process = Runtime.getRuntime().exec(protocCMD); 20 process.waitFor(); 21 int exitValue = process.exitValue(); 22 if (exitValue != 0) { 23 System.out.println("protoc execute failed"); 24 return; 25 } 26 Descriptors.Descriptor pbDescritpor = null; 27 DescriptorProtos.FileDescriptorSet descriptorSet = DescriptorProtos.FileDescriptorSet.parseFrom(new FileInputStream("./cinema.description")); 28 for (DescriptorProtos.FileDescriptorProto fdp : descriptorSet.getFileList()) { 29 Descriptors.FileDescriptor fileDescriptor = Descriptors.FileDescriptor.buildFrom(fdp, new Descriptors.FileDescriptor[]{}); 30 for (Descriptors.Descriptor descriptor : fileDescriptor.getMessageTypes()) { 31 if (descriptor.getName().equals("Movie")) { 32 System.out.println("Movie descriptor found"); 33 pbDescritpor = descriptor; 34 break; 35 } 36 } 37 } 38 if (pbDescritpor == null) { 39 System.out.println("No matched descriptor"); 40 return; 41 } 42 DynamicMessage.Builder pbBuilder = DynamicMessage.newBuilder(pbDescritpor); 43 44 Message pbMessage = pbBuilder.mergeFrom(buffer3).build(); 45 System.out.println(pbMessage); 46 47 } catch (Exception e) { 48 System.out.println("Exception"); 49 e.printStackTrace(); 50 } 51 }Main.java 執行結果:
Dynamic Message Parse From byte array
[B@597ccf6e
Movie descriptor found
name: "The Shining"
type: ADULT
releaseTimeStamp: 327859200