2012-04-14 4 views
8

Ich überprüft, aber scheinen nicht zu sehen, wie direkt eine Klasse zu einem Byte-Array serialisieren und anschließend aus einem Byte-Array mit Marc Gravell's Protobuf-Net-Implementierung deserialisieren.Protobuf-net Serialisierung/Deserialisierung

Edit: Ich änderte die Frage und stellte Code zur Verfügung, weil die ursprüngliche Frage, wie man in Byte [] serialisiert, ohne durch Strom zu gehen, zugegebenermaßen trivial war. Entschuldigen Sie.

Aktualisierte Frage: Gibt es eine Möglichkeit, sich nicht mit Generika zu befassen und stattdessen den Typ der Eigenschaft "MessageBody" durch Reflexion abzuleiten, wenn es den Konstruktor passiert? Ich nehme an, ich kann den Objekttyp nicht serialisieren, richtig? Die aktuelle Lösung sieht sehr umständlich aus, da ich jedes Mal, wenn ich eine neue Nachricht instanziiere, den Typ des MessageBody übergeben muss. Gibt es dafür eine schlankere Lösung?

ich kam mit den folgenden:

class Program 
{ 
    static void Main(string[] args) 
    { 
     Message<string> msg = new Message<string>("Producer", "Consumer", "Test Message"); 

     byte[] byteArray = msg.Serialize(); 
     Message<string> message = Message<string>.Deserialize(byteArray); 

     Console.WriteLine("Output"); 
     Console.WriteLine(message.From); 
     Console.WriteLine(message.To); 
     Console.WriteLine(message.MessageBody); 

     Console.ReadLine(); 

    } 
} 

[ProtoContract] 
public class Message<T> 
{ 
    [ProtoMember(1)] 
    public string From { get; private set; } 
    [ProtoMember(2)] 
    public string To { get; private set; } 
    [ProtoMember(3)] 
    public T MessageBody { get; private set; } 

    public Message() 
    { 

    } 

    public Message(string from, string to, T messageBody) 
    { 
     this.From = from; 
     this.To = to; 
     this.MessageBody = messageBody; 
    } 

    public byte[] Serialize() 
    { 
     byte[] msgOut; 

     using (var stream = new MemoryStream()) 
     { 
      Serializer.Serialize(stream, this); 
      msgOut = stream.GetBuffer(); 
     } 

     return msgOut; 
    } 

    public static Message<T> Deserialize(byte[] message) 
    { 
     Message<T> msgOut; 

     using (var stream = new MemoryStream(message)) 
     { 
      msgOut = Serializer.Deserialize<Message<T>>(stream); 
     } 

     return msgOut; 
    } 
} 

Was ich zu bekommen ist etwas, wie:

Nachricht newMsg = new Message ("Producer", "Consumer", Foo); byte [] ByteArray = newMsg.Serialize();

und Nachricht msg = Message.Deserialize (byteArray);

(wobei Deserialize eine statische Methode ist und immer in ein Objekt vom Typ Message deserialisiert wird und nur wissen muss, in welchen Typ der Nachrichtentext deserialisiert werden soll).

+0

nicht Protobuf.net Open-Source? –

+0

Es ist, aber ich möchte nicht die Quelle anpassen, weil ich gerne mit neuen Versionen Schritt halten, ohne nachfolgende Anpassungen vornehmen zu müssen, weil die Bibliothek nur eine sehr kleine Komponente als Teil von viel größeren Projekten ist. –

+0

Ein 'MemoryStream' ist nur ein verkettetes Byte-Array, was ist das Problem, das zu verwenden? –

Antwort

8

Es gibt ein paar verschiedene Fragen hier, also werde ich antworten, was ich sehen kann: Wenn ich etwas verpasst habe, lass es mich wissen.

Erstens, wie bereits erwähnt, ist ein MemoryStream der gängigste Weg, um zu einem Byte [] zu gelangen. Dies ist konsistent mit den meisten Serialisierern - zum Beispiel haben XmlSerializer, BinaryFormatter und DataContractSerializer auch keine "als ein Byte [] Überladung", aber akzeptiert MemoryStream.

Generika: Sie müssen keine Generika verwenden; v1 verfügt über Serializer.NonGeneric, die das von Ihnen weg umbricht. In v2 ist der "Kern" nicht-generisch und kann über RuntimeTypeModel.Default zugegriffen werden; natürlich Serializer und Serializer.NonGeneric weiterhin zu arbeiten.

Für den Fall, dass der Typ enthalten sein muss: ja, die probobuf-Spezifikation geht davon aus, dass der Empfänger weiß, welche Art von Daten er erhält. Eine einfache Option ist die Verwendung eines einfachen Wrapper-Objekts als "Root" -Objekt mit mehreren typisierten Eigenschaften für die Daten (von denen nur eines nicht null ist). Eine andere Option könnte aus der eingebauten Vererbungsunterstützung über ProtoInclude stammen (Anmerkung: als ein Implementierungsdetail sind diese beiden Ansätze identisch).

In Ihrem speziellen Beispiel, vielleicht betrachten:

[ProtoContract] 
[ProtoInclude(1, typeof(Message<Foo>))] 
.... More as needed 
[ProtoInclude(8, typeof(Message<Bar>))] 
public abstract class Message 
{ } 
[ProtoContract] 
public class Message<T> : Message 
{ 
    ... 
} 

Dann serialisiert nur mit <Message> - die API automatisch die richtige Art zu schaffen.

Mit den jüngsten baut, gibt es auch eine DynamicType Option, die Typ-Daten für Sie, zum Beispiel umfasst:

[ProtoContract] 
public class MyRoot { 
    [ProtoMember(1, DynamicType=true)] 
    public object Value { get; set; } 
} 

Dies wird für jeden Wert arbeiten, die einen Vertrag-Typ-Instanz hält (aber nicht für Primitive, und idealerweise keine Vererbung).

+0

Marc, danke für die Kommentare. Der letzte Ihrer Vorschläge sieht genau so aus, wie ich gesucht habe. Lassen Sie mich ein wenig damit spielen und zu Ihnen zurückkommen. –

+0

@Freddy Ich hätte dem ProtoInclude einen persönlich empfohlen, aber: was auch immer funktioniert ... –

+1

Mit Blick auf beide ... tausend Dank für Ihre Beratung. Protobuf-net ist übrigens eine geniale Bibliothek –

3

Der Code, den der OP gepostet hat, würde für mich nicht ganz funktionieren, das folgende ist eine leichte Anpassung, die ein wenig mehr von Marc Gravells Vorschlägen mitnimmt. Das Vererben von Message wurde benötigt, um zu verhindern, dass "zyklische Vererbung nicht erlaubt ist", und wie in den Code-Kommentaren unten erwähnt, funktionierte GetBuffer auch nicht.

der Hoffnung, hilft es jemand anderes, hat mich ein paar Stunden alles zur Arbeit zu kommen ...



     [ProtoContract] 
     public abstract class Message 
     { 
     public byte[] Serialize() 
     { 
      byte[] result; 
      using (var stream = new MemoryStream()) 
      { 
      Serializer.Serialize(stream, this); 
      result = stream.ToArray(); //GetBuffer was giving me a Protobuf.ProtoException of "Invalid field in source data: 0" when deserializing 
      } 
      return result; 
     } 
     } 

     [ProtoContract] 
     public class Message : Message 
     { 
     [ProtoMember(1)] 
     public string From { get; private set; } 
     [ProtoMember(2)] 
     public string To { get; private set; } 
     [ProtoMember(3)] 
     public T MessageBody { get; private set; } 

     public Message() 
     { } 

     public Message(string from, string to, T messageBody) 
     { 
      this.From = from; 
      this.To = to; 
      this.MessageBody = messageBody; 
     } 

     public static Message Deserialize(byte[] message) 
     { 
      Message result; 
      using (var stream = new MemoryStream(message)) 
      { 
      result = Serializer.Deserialize>(stream); 
      } 
      return result; 
     } 
     } 

+0

interessiert, aber Syntaxfehler in der Nähe Deserialize – bunt

+0

Der Fehler ist ein Markup-Problem: Serializer.Deserialize > (Stream); – 9swampy