2012-04-14 16 views
8

Comprobé pero parece que no puedo ver cómo serializar directamente una clase en una matriz de bytes y posteriormente deserializar de una matriz de bytes utilizando la implementación de protobuf-net de Marc Gravell.Serialización/deserialización de Protobuf-net

Editar: Cambié la pregunta y proporcioné el código porque la cuestión original de cómo serializar en byte [] sin tener que pasar por la transmisión era ciertamente trivial. Mis disculpas.

Pregunta actualizada: ¿Hay alguna forma de no tener que tratar con los genéricos y en su lugar inferir el tipo de la propiedad "MessageBody" a través de la reflexión cuando se pasa a través del constructor? Supongo que no puedo serializar el tipo de objeto, ¿correcto? La solución actual parece muy engorrosa porque necesito pasar el tipo del MessageBody cada vez que crea una instancia de un nuevo mensaje. ¿Hay una solución más elegante para esto?

me ocurrió lo siguiente:

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

Lo que me gustaría llegar a algo como:

mensaje newMsg = nuevo mensaje ("Productor", "consumidor", Foo); byte [] byteArray = newMsg.Serialize();

y Message msg = Message.Deserialize (byteArray);

(donde Deserializar es un método estático y siempre se deserializa en un objeto de tipo Mensaje y solo necesita saber en qué tipo deserializar el cuerpo del mensaje).

+0

no se Protobuf.net de código abierto? –

+0

Es pero no quiero ajustar la fuente porque me gusta mantener el ritmo con las nuevas versiones sin tener que realizar ajustes posteriores porque la biblioteca es solo un componente muy pequeño como parte de proyectos mucho más grandes. –

+0

Un 'MemoryStream' es solo una matriz de bytes disfrazada, ¿cuál es el problema de usar eso? –

Respuesta

8

aquí hay algunas preguntas diferentes, así que responderé a lo que puedo ver: si me he perdido algo, házmelo saber.

En primer lugar, como se señaló, un MemoryStream es la forma más común de llegar a un byte []. Esto es coherente con la mayoría de los serializadores; por ejemplo, XmlSerializer, BinaryFormatter y DataContractSerializer también no tienen una sobrecarga "como un byte []", pero aceptan MemoryStream.

Genéricos: no necesita usar genéricos; v1 tiene Serializer.NonGeneric, que lo aleja de usted. En v2, el "núcleo" no es genérico, y se puede acceder a través de RuntimeTypeModel.Default; por supuesto Serializer y Serializer.NonGeneric siguen funcionando.

Para el problema de tener que incluir el tipo: sí, la especificación protobuf supone que el receptor sabe qué tipo de datos se les está dando. Una opción simple aquí es usar un objeto contenedor simple como el objeto "raíz", con múltiples propiedades tipadas para los datos (solo uno de los cuales no es nulo). Otra opción podría surgir del soporte de herencia incorporado a través de ProtoInclude (nota: como un detalle de implementación, estos dos enfoques son idénticos).

En el ejemplo específico, tal vez considerar:

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

A continuación, sólo serializar con <Message> - la API va a crear el tipo correcto de forma automática.

Con ejecuciones recientes, también hay una opción DynamicType que incluye datos de tipo para usted, por ejemplo:

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

Esto funcionará para cualquier valor que tiene una instancia de tipo de contrato (pero no para primitivas, e idealmente no involucrando herencia).

+0

Marc, gracias por los comentarios. La última de tus sugerencias parece exactamente lo que estaba buscando. Déjame jugar un poco y responderte. –

+0

@Freddy Le habría aconsejado al ProtoInclude uno personalmente, pero: lo que sea que funcione ... –

+1

Mirando ambos ... muchas gracias por su consejo. Protobuf-net es una impresionante biblioteca, por cierto, –

3

El código que OP publicó no funcionaría para mí, la siguiente es una pequeña adaptación que incorpora un poco más de las sugerencias de Marc Gravell. Heredar de Mensaje era necesario para evitar que "La herencia cíclica no esté permitida", y como se señala en el código, los comentarios a continuación GetBuffer tampoco funcionaban.

Con la esperanza de que ayude a alguien más, me llevó unas cuantas horas para conseguir que todo funcione ...



     [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

interesado, pero el error de sintaxis cerca de Deserialize – bunt

+0

El error es un problema de marcado: Serializer.Deserialize > (transmisión); – 9swampy

Cuestiones relacionadas