2015-09-16 33 views
7

Mi piacerebbe sapere come trasformare un Protobuf Qualsiasi tipo nel tipo di messaggio Protobuf originale e viceversa. In Java da messaggio a qualsiasi è facile:Protobuf 3.0 Qualsiasi Tipo pack/unpack

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

Ma come posso analizzare che Qualsiasi torna al messaggio originial (ad esempio per il tipo di "protoMess")? Probabilmente potrei analizzare tutto su un flusso solo per leggerlo di nuovo, ma non è quello che voglio. Voglio avere una trasformazione come questa:

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

Come posso ottenere quello? È già implementato per Java? Il Protobuf Language Guide dice che c'erano i metodi pack e unpack, ma non ce ne sono in Java. Grazie in anticipo :)

risposta

5

La risposta potrebbe essere un po 'in ritardo ma forse questo aiuta ancora qualcuno.

Nella versione corrente di Protocol Buffers 3 pack e unpack sono available in Java.

Nel tuo esempio di imballaggio può essere fatto come:

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

e disfare come:

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

Anche qui è un esempio completo per la gestione dei messaggi di protocollo buffer con annidati Any messaggi:

Protocolli File buffer

un semplice file buffer di protocollo con un Any messaggio nidificato potrebbe essere simile:

syntax = "proto3"; 

import "google/protobuf/any.proto"; 

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

Un possibile messaggio nidificato potrebbe quindi essere:

syntax = "proto3"; 

message ChildMessage { 
    string text = 1; 
} 

imballaggio

Per costruire l'intero messaggio del può essere utilizzata la seguente funzione:

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(); 
} 

disimballaggio

Per leggere il messaggio bambino dal messaggio genitore la seguente funzione può essere utilizzata:

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

EDIT:

Se i messaggi confezionati possono avere diversi tipi, si può leggere il typeUrl e utilizzare la riflessione per decomprimere il messaggio.Supponendo di avere i messaggi bambino ChildMessage1 e ChildMessage2 è possibile effettuare le seguenti operazioni:

@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; 
    } 
} 

Per ulteriori elaborazioni, è possibile determinare il tipo del messaggio con instanceof, che non è molto efficiente. Se si desidera ottenere un messaggio di un certo tipo, si dovrebbe confrontare il typeUrl direttamente:

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

Non c'è altra via che questa 'readChildMessage'? Cosa succede se ci sono decine di messaggi diversi che potrebbero entrare? Basta aggiungere nuovi blocchi 'try-catch'? Anche 'switch-case' e simili sono assolutamente inaccettabili. – Sorona

+0

Buona domanda, ho dimenticato di aggiungere che è possibile ottenere il nome del messaggio imballato come 'typeURL'. Ciò consente di decomprimere qualsiasi messaggio tramite riflessione o decidere direttamente cosa fare con il messaggio. Ho aggiunto due esempi alla mia risposta, spero che questo aiuti. – sundance

+0

Ottimo! Mi ha aiutato! – FisherCoder