2015-09-16 66 views
7

我想知道如何轉換Protobuf 任何類型爲原始Protobuf消息類型,反之亦然。在Java中從信息給任何容易:Protobuf 3.0任何類型打包/解包

Any.Builder anyBuilder = Any.newBuilder().mergeFrom(protoMess.build()); 

但如何可以分析任何回originial消息(例如,以「protoMess」的類型)?我可能會解析流中的所有內容,只是爲了讀取它,但這不是我想要的。我想要這樣一些轉換:

ProtoMess.MessData.Builder protoMessBuilder = (ProtoMess.MessData.Builder) transformToMessageBuilder(anyBuilder) 

我該如何做到這一點?它是否已經實現了Java? Protobuf Language Guide表示有pack和unpack方法,但Java中沒有。 預先感謝:)

回答

5

答案可能有點晚,但也許這仍然有助於某人。

在當前版本的協議緩衝區3 packunpackavailable in Java

在你的榜樣包裝可以像做:

Any anyMessage = Any.pack(protoMess.build())); 

和拆包,如:

ProtoMess protoMess = anyMessage.unpack(ProtoMess.class); 

這裏也是處理與嵌套Any消息Protocol Buffers的消息的完整的例子:

ProtocolBuffers文件

一個簡單的協議緩衝器,具有嵌套Any消息文件可能看起來像:

syntax = "proto3"; 

import "google/protobuf/any.proto"; 

message ParentMessage { 
    string text = 1; 
    google.protobuf.Any childMessage = 2; 
} 

一個可能的嵌套消息,然後可以是:

syntax = "proto3"; 

message ChildMessage { 
    string text = 1; 
} 

包裝

要建立完整的消息可以使用以下功能:

public ParentMessage createMessage() { 
    // Create child message 
    ChildMessage.Builder childMessageBuilder = ChildMessage.newBuilder(); 
    childMessageBuilder.setText("Child Text"); 
    // Create parent message 
    ParentMessage.Builder parentMessageBuilder = ParentMessage.newBuilder(); 
    parentMessageBuilder.setText("Parent Text"); 
    parentMessageBuilder.setChildMessage(Any.pack(childMessageBuilder.build())); 
    // Return message 
    return parentMessageBuilder.build(); 
} 

開箱

讀取從父消息可用於以下功能的子消息:

public ChildMessage readChildMessage(ParentMessage parentMessage) { 
    try { 
     return parentMessage.getChildMessage().unpack(ChildMessage.class); 
    } catch (InvalidProtocolBufferException e) { 
     e.printStackTrace(); 
     return null; 
    } 
} 

編輯:

如果打包的消息可以有不同的類型,可以讀出typeUrl並使用反射來解開消息。假設你有孩子的消息ChildMessage1ChildMessage2你可以做到以下幾點:

@SuppressWarnings("unchecked") 
public Message readChildMessage(ParentMessage parentMessage) { 
    try { 
     Any childMessage = parentMessage.getChildMessage(); 
     String clazzName = childMessage.getTypeUrl().split("/")[1]; 
     String clazzPackage = String.format("package.%s", clazzName); 
     Class<Message> clazz = (Class<Message>) Class.forName(clazzPackage); 
     return childMessage.unpack(clazz); 
    } catch (ClassNotFoundException | InvalidProtocolBufferException e) { 
     e.printStackTrace(); 
     return null; 
    } 
} 

作進一步處理,你能確定消息與instanceof的類型,這是不是很有效。如果你想獲得某種類型的消息,你應該比較typeUrl直接:

public ChildMessage1 readChildMessage(ParentMessage parentMessage) { 
    try { 
     Any childMessage = parentMessage.getChildMessage(); 
     String clazzName = childMessage.getTypeUrl().split("/")[1]; 
     if (clazzName.equals("ChildMessage1")) { 
      return childMessage.unpack("ChildMessage1.class"); 
     } 
     return null 
    } catch (InvalidProtocolBufferException e) { 
     e.printStackTrace(); 
     return null; 
    } 
} 
+1

難道就沒有其他辦法比這'readChildMessage'?如果我有幾十條不同的消息可以進來,會怎麼樣?只需添加新的'try-catch'塊?即使是「開關盒」等也是絕對不能接受的。 – Sorona

+0

好問題,我忘了補充一下,你可以把打包的消息的名字作爲'typeURL'。這允許通過反射來解壓縮任何消息或直接決定如何處理消息。我在回答中添加了兩個示例,我希望這有助於。 – sundance

+0

太棒了!它幫助了我! – FisherCoder