2011-09-30 7 views
14

que he leído en alguna parte un comentario por el autor de ProtoBuf.NET que:la serialización con ProtoBuf.NET sin que los miembros de etiquetado

hay opciones para inferir automáticamente los números, pero que es frágil y no se recomienda. Solo use esto si sabe que no necesita agregar más miembros (los ordena alfabéticamente, por lo que agregar un nuevo AardvarkCount lo romperá todo).

Esto es exactamente ese tipo de situación en la que estoy interesado en :)

tengo algo que es similar a un mapa a reducir escenario donde quiero serializar los resultados generados en equipos remotos que utilizan búferes de protocolo (por ejemplo, el lado "mapa" de map-reduce) y luego leerlos y combinar esos resultados para un procesamiento posterior (por ejemplo, el lado "reducir").

No quiero iniciar un maratón de decoración de atributos sobre todas las clases posibles que tengo que puedan serializarse durante este proceso, y creo que los búferes de protocolo son muy atractivos ya que puedo crear resultados con Mono y consumirlos sin esfuerzo en MS.NET y viceversa ...

Las desventajas aparentes de no etiquetar previamente a los miembros no me molestan ya que exactamente la misma revisión de software genera/consume, así que no necesito preocuparme sobre nuevos miembros apareciendo en el código y jugando todo mi esquema ...

En resumen, mi pregunta es:

  • ¿Cómo lo hago (serializar con ProtoBuf.NET sin etiquetar/crear clases Meta por mi cuenta)?
  • ¿Hay algún agujero en mi esquema que me haya pasado por alto?
+0

¿Tiene que almacenar esos resultados en cualquier momento o los datos están siempre en vivo? ¿Y qué tan seguro puede estar de que los dos extremos estén sincronizados? Aparte de eso, parece que debería funcionar bien ... –

Respuesta

8

Si se puede vivir con un solo atributo , entonces el truco es:

[ProtoContract(ImplicitFields = ImplicitFields.AllPublic)] 
    public class WithImplicitFields 
    { 
     public int X { get; set; } 
     public string Y { get; set; } 
    } 

hay 2 opciones aquí; AllPublic funciona como XmlSerializer - las propiedades públicas y los campos se serializan (usando el orden alfabético para elegir números de etiqueta); AllFields funciona un poco como BinaryFormatter - los campos están serializados (de nuevo, alfabéticos).

No puedo recordar si esto todavía está disponible en la API v2; ¡Sé que está en mi lista de cosas para garantizar el trabajo! Pero si lo quiere en v2 sin atributos, estoy seguro de que puedo agregar una sobrecarga de Add(ImplicitFields).

Siempre que los 2 extremos nunca estén fuera de paso, está bien. Si almacena los datos, o no versiona los dos extremos "en el paso", entonces podría haber problemas. Ver también los comentarios intellisense en la enumeración (que prácticamente repite la advertencia de la que ya eres consciente).

+0

Gracias Marc. Comienzo a ver señales de vida con ProtoContract en la definición de clase, y estoy usando v2 en este momento ... Estoy tratando de hacer que esto funcione sin atributos, lo que sea, estaré encantado de obtener un Add (ImplicitFields)) Sobrecarga ya que mantiene el código mucho más limpio ... – damageboy

+1

Un deseo más que tendría, es que ImplicitFields sea "recursivo", es decir, si tengo una clase X que contiene una Lista , y uso Agregar (ImplicitFields)) que me gustaría que esto se propagara a lo largo de la cadena de clases ... – damageboy

+0

@ damageboy quizás la forma de abordarlo sea en el nivel de TypeModel, entonces podría aparecer como un modelo tipo que no funciona de esa manera. Eso es en realidad ahora funciona InferByName (similar pero sutilmente diferente). –

1

Tuve el mismo problema y la forma en que lo resolví con TypeModel. Se basa en propiedades ordenadas por su nombre (sin embargo, no comprueba la existencia de la propiedad setter/getter o serializar-capacidad de un tipo determinado):

[TestFixture] 
public class InferredProtoPoc 
{ 
    [Test] 
    public void UsageTest() 
    { 
     var model = TypeModel.Create(); 
     // Dynamically create the model for MyPoco 
     AddProperties(model, typeof(MyPoco)); 
     // Display the Generated Schema of MyPoco 
     Console.WriteLine(model.GetSchema(typeof(MyPoco))); 

     var instance = new MyPoco 
     { 
      IntegerProperty = 42, 
      StringProperty = "Foobar", 
      Containers = new List<EmbeddedPoco> 
      { 
       new EmbeddedPoco { Id = 12, Name = "MyFirstOne" }, 
       new EmbeddedPoco { Id = 13, Name = "EmbeddedAgain" } 
      } 
     }; 

     var ms = new MemoryStream(); 
     model.Serialize(ms, instance); 
     ms.Seek(0, SeekOrigin.Begin); 
     var res = (MyPoco) model.Deserialize(ms, null, typeof(MyPoco)); 
     Assert.IsNotNull(res); 
     Assert.AreEqual(42, res.IntegerProperty); 
     Assert.AreEqual("Foobar", res.StringProperty); 
     var list = res.Containers; 
     Assert.IsNotNull(list); 
     Assert.AreEqual(2, list.Count); 
     Assert.IsTrue(list.Any(x => x.Id == 12)); 
     Assert.IsTrue(list.Where(x => x.Id == 12).Any(x => x.Name == "MyFirstOne")); 
     Assert.IsTrue(list.Any(x => x.Id == 13)); 
     Assert.IsTrue(list.Where(x => x.Id == 13).Any(x => x.Name == "EmbeddedAgain")); 
    } 

    private static void AddProperties(RuntimeTypeModel model, Type type) 
    { 
     var metaType = model.Add(type, true); 
     foreach (var property in type.GetProperties().OrderBy(x => x.Name)) 
     { 
      metaType.Add(property.Name); 
      var propertyType = property.PropertyType; 
      if (!IsBuiltinType(propertyType) && 
       !model.IsDefined(propertyType) && 
       propertyType.GetProperties().Length > 0) 
      { 

       AddProperties(model, propertyType); 
      } 
     } 
    } 

    private static bool IsBuiltinType(Type type) 
    { 
     return type.IsValueType || type == typeof (string); 
    } 
} 

public class MyPoco 
{ 
    public int IntegerProperty { get; set; } 
    public string StringProperty { get; set; } 
    public List<EmbeddedPoco> Containers { get; set; } 
} 

public class EmbeddedPoco 
{ 
    public int Id { get; set; } 
    public String Name { get; set; } 
} 

Y eso es lo que se obtiene a partir de ejecutarlo.

message EmbeddedPoco { 
     optional int32 Id = 1; 
     optional string Name = 2; 
    } 
    message MyPoco { 
     repeated EmbeddedPoco Containers = 1; 
     optional int32 IntegerProperty = 2; 
     optional string StringProperty = 3; 
    } 

Para obtener un rendimiento, se puede optar por compilar el TypeModel, y/o almacenar el proto generado para usos futuros. Sin embargo, ten en cuenta que la dependencia oculta de Protocol Buffer podría ser peligrosa a largo plazo si evoluciona el poco (objeto antiguo de contenedor simple).

Cuestiones relacionadas