2015-09-16 13 views
7

Ich würde gerne wissen, wie man einen Protobuf Any Typ auf den ursprünglichen Protobuf-Nachrichtentyp und umgekehrt umwandelt. In Java von Nachricht Jeder ist einfach:Protobuf 3.0 Beliebige Art packen/entpacken

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

Aber wie kann ich analysieren, dass Jede zurück zum originial Nachricht (zum Beispiel nach Art der „protoMess“)? Ich könnte wahrscheinlich alles in einem Stream parsen, nur um es wieder einzulesen, aber das ist nicht das, was ich will. Ich möchte eine Transformation wie folgt haben:

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

Wie kann ich das erreichen? Ist es bereits für Java implementiert? Die Protobuf Language Guide sagt, es gab Pack-und Entpackungsmethoden, aber es gibt keine in Java. Vielen Dank im Voraus :)

Antwort

5

Die Antwort könnte ein bisschen spät sein, aber vielleicht hilft dies noch jemand.

In der aktuellen Version der Protokollpuffer 3 pack und unpack sind available in Java.

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

und Auspacken wie:

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

Hier ist auch ein vollständiges Beispiel für den Umgang mit Protocol Buffers Nachrichten mit verschachtelten Any Nachrichten

In Ihrem Beispiel Verpackung kann wie geschehen:

ProtocolBuffers Dateien

Ein einfaches Protocol Buffers mit einer verschachtelten Any Nachricht Datei aussehen könnte:

syntax = "proto3"; 

import "google/protobuf/any.proto"; 

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

Eine mögliche verschachtelte Nachricht könnte dann sein:

syntax = "proto3"; 

message ChildMessage { 
    string text = 1; 
} 

Verpackung

Um die vollständige Nachricht baut die Folgende Funktion kann verwendet werden:

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

Auspacken

Um die Kinder Nachricht von der übergeordneten Nachricht die folgende Funktion verwendet werden kann, lesen:

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

EDIT:

Wenn Ihre gepackten Nachrichten verschiedene Typen haben, können Sie Lesen Sie die typeUrl aus und verwenden Sie die Reflektion, um die Nachricht zu entpacken.Vorausgesetzt, dass Sie die Kindnachrichten haben ChildMessage1 und ChildMessage2 können Sie folgendes tun:

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

Zur weiteren Verarbeitung können Sie die Art der Nachricht mit instanceof bestimmen, die nicht sehr effizient ist. Wenn Sie eine Nachricht eines bestimmten Typs erhalten möchten, sollten Sie die typeUrl direkt vergleichen:

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

Gibt es keine andere Möglichkeit, als diese 'readChildMessage'? Was, wenn ich dutzende verschiedene Nachrichten habe, die reinkommen könnten? Fügen Sie einfach neue "try-catch" Blöcke hinzu? Selbst 'switch-case' und ähnliches ist absolut inakzeptabel. – Sorona

+0

Gute Frage, ich habe vergessen hinzuzufügen, dass Sie den Namen der gepackten Nachricht als 'typeURL' erhalten können. Dies ermöglicht es, jede Nachricht per Reflexion auszupacken oder direkt zu entscheiden, was mit der Nachricht geschehen soll. Ich habe meiner Antwort zwei Beispiele hinzugefügt, ich hoffe, das hilft. – sundance

+0

Großartig! Es hat mir geholfen! – FisherCoder