Protobuf動態解析在Java中的應用 包含例子程式

来源:http://www.cnblogs.com/liulaoshi/archive/2017/07/23/7226439.html
-Advertisement-
Play Games

最近在做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

  解釋具體過程: 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的類名    轉載請註明出處。歡迎拍磚~      
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 在上一篇的EF之DB First中,存在以下的兩個問題: 1. 添加/編輯頁面顯示的是屬性名稱,而非自定義的名稱(如:姓名、專業...) 2. 添加/編輯時沒有加入驗證 3. 數據展示使用分頁 @Html.LabelFor(model => model.Name, htmlAttributes: n ...
  • yaml文件處理(http://pyyaml.org/wiki/PyYAMLDocumentation) 摘要: 本文講的是yaml在python上的使用教程詳解, YAML是一種容易人類閱讀、適合表示程式語言的數據結構、可用於不同程式間交換數據、支持泛型工具、支持串列處理、豐富的表達能力和可擴展性 ...
  • Numpy指南筆記 第2章:Numpy基礎 創建多維數組# coding:utf-8import numpy as npm=np.array([np.arange(2),np.arange(2)])print mprint m.shape 一維數組切片和索引# coding:utf-8import ...
  • 流 數據流 用於傳輸數據。IO流 Input/Output流。數據從外部流向程式 輸入流;數據從程式流向外部的時候--輸出流。讀取一個文件 數據從文件流向程式 輸入流;向一個文件中寫入數據 數據從程式流向文件 輸出流 根據數據流動的方向:輸入流/輸出流 根據數據傳輸的形式:位元組流/字元流 輸入流 輸 ...
  • 要求: Readme: 運行程式,輸入薪水,根據商品列表的序號選擇購買的商品,可以選擇多次購買,或者不購買 流程圖: 代碼: ...
  • #列印實心菱形,思路:分上下,i控制行,j控制列印的空格數,k控制列印的*數量row = int(input("please input a raw number: "))for i in range(row): #i控制行數 for j in range(row-1-i):#空格數和行數的關係 p ...
  • XML文件處理 XML文件處理,有好幾種方式,這裡介紹一下xml.etree.ElementTree as ET。 註意:xml.etree.ElementTree模塊在應對惡意結構數據時顯得並不安全。 每個element對象都具有以下屬性: 1. tag:string對象,表示數據代表的種類; 2 ...
  • 一.ServletContext 介面(javax.servlet)定義:public interface ServletContext原理: Tomcat啟動的時候,需要識別webapps下的各個WEB應用,識別各個WEB應用的同時為每個WEB應用創建對應的對象ServletContext,一個W... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...