2012-04-14 8 views
8

Ho controllato ma non riesco a vedere come serializzare direttamente una classe in un array di byte e successivamente deserializzare da un array di byte usando l'implementazione protobuf-net di Marc Gravell.Serializzazione/deserializzazione di Protobuf-net

Modifica: ho modificato la domanda e fornito il codice perché la domanda originale su come serializzare in byte [] senza dover passare attraverso il flusso era dichiaratamente banale. Mie scuse.

Domanda aggiornata: Esiste un modo per non avere a che fare con i generici e invece inferire il tipo di proprietà "MessageBody" attraverso il reflection quando viene passato attraverso il costruttore? Suppongo di non poter serializzare il tipo di oggetto, corretto? La soluzione attuale sembra molto complicata in quanto devo passare il tipo di MessageBody ogni volta che istanziato un nuovo messaggio. C'è una soluzione più sottile a questo?

mi si avvicinò con il seguente:

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

Quello che mi piace per arrivare a qualcosa come:

Messaggio newMsg = new Message ("Produttore", "consumatore", Foo); byte [] byteArray = newMsg.Serialize();

e Messaggio msg = Message.Deserialize (byteArray);

(dove Deserialize è un metodo statico e deserializza sempre in un oggetto di tipo Message e deve solo conoscere il tipo di deserializzazione del corpo del messaggio in).

+0

non è Protobuf.net open-source? –

+0

È, ma non voglio regolare la sorgente perché mi piace stare al passo con le nuove versioni senza dover apportare modifiche successive perché la libreria è solo un componente molto piccolo come parte di progetti molto più grandi. –

+0

Un 'MemoryStream' è solo una matrice di byte sotto mentite spoglie, qual è il problema di usarlo? –

risposta

8

qui ci sono alcune domande diverse, quindi risponderò a quello che posso vedere: se ho perso qualcosa fammelo sapere.

In primo luogo, come notato, un MemoryStream è il modo più comune di raggiungere un byte []. Ciò è coerente con la maggior parte dei serializzatori - ad esempio, XmlSerializer, BinaryFormatter e DataContractSerializer anche non hanno un "sovraccarico di byte []", ma accettano MemoryStream.

Generici: non è necessario utilizzare i generici; v1 ha Serializer.NonGeneric, che lo avvolge lontano da te. Nella v2, il "core" non è generico e può essere richiamato tramite RuntimeTypeModel.Default; ovviamente Serializer e Serializer.NonGeneric continuano a funzionare.

Per il problema di dover includere il tipo: sì, la specifica di protobuf presuppone che il destinatario sappia che tipo di dati vengono dati. Un'opzione semplice qui è quella di utilizzare un semplice oggetto wrapper come oggetto "root", con proprietà multiple digitate per i dati (solo uno dei quali non è nulla). Un'altra opzione potrebbe derivare dal supporto ereditario integrato tramite ProtoInclude (nota: come dettaglio di implementazione, questi due approcci sono identici).

Nel tuo esempio specifico, forse prendere in considerazione:

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

Poi basta serializzare con <Message> - l'API creerà il giusto tipo automaticamente.

Con la recente build, c'è anche un DynamicType opzione che include dati di tipo per voi, per esempio:

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

Questo funziona per qualsiasi valore che contiene un'istanza di contratto tipo (ma non per primitivi e idealmente non coinvolgono l'ereditarietà).

+0

Marc, grazie per i commenti. L'ultimo dei tuoi suggerimenti sembra esattamente quello che stavo cercando. Lasciami giocare un po 'e torna da te. –

+0

@Freddy Avrei consigliato personalmente il ProtoInclude, ma: qualunque cosa funzioni ... –

+1

Guardando entrambi ... grazie mille per i tuoi consigli. Protobuf-net è una libreria fantastica a proposito –

3

Il codice che l'OP ha pubblicato non avrebbe funzionato per me, il seguente è un leggero adattamento che riprende un po 'più i suggerimenti di Marc Gravell. Ereditato dal messaggio era necessario per evitare che "l'ereditarietà ciclica non fosse consentita" e, come indicato nei commenti sul codice di seguito, anche GetBuffer non funzionava.

Sperando che aiuta a qualcun altro, mi ha portato una buona poche ore per avere tutto a lavorare ...



     [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

interessato ma errore di sintassi nei pressi di Deserialize – bunt

+0

L'errore è un problema di markup: Serializer.Deserialize > (flusso); – 9swampy