c# .net serialization protobuf-net

c# - protobuf-net: serializar una lista vacía



.net serialization (3)

tenemos algunos problemas con la serialización de una lista vacía. aquí algún código en .NET usando CF 2.0

//Generating the protobuf-msg ProtoBufMessage msg = new ProtoBufMessage(); msg.list = new List<AnotherProtobufMessage>(); // Serializing and sending throw HTTP-POST MemoryStream stream = new MemoryStream(); Serializer.Serialize(stream, msg); byte[] bytes = stream.ToArray(); HttpWebRequest request = createRequest(); request.ContentLength = bytes.Length ; using (Stream httpStream = request.GetRequestStream()) { httpStream.Write(bytes, 0, bytes.Length); }

obtuvimos una excepción cuando intentamos escribir en la transmisión (bytes.length fuera de rango). Pero un tipo con una lista vacía no debe tener 0 bytes, a la derecha (¿tipo de información?)?

Necesitamos este tipo de envío, porque en la respuesta están los mensajes del servidor para nuestro cliente.


Como dijo @Marc, el formato de conexión solo envía datos para los artículos, por lo que para saber si la lista está vacía o nula, debe agregar ese bit de información a la transmisión.
Agregar propiedad adicional para indicar si la colección original estaba vacía o no es fácil, pero si no desea modificar la definición de tipo original, tiene otras dos opciones:

Serializar usando Surrogate

El tipo sustituto tendrá la propiedad adicional (manteniendo intacto su tipo original) y restaurará el estado original de la lista: nulo, con elementos o vacío.

[TestMethod] public void SerializeEmptyCollectionUsingSurrogate_RemainEmpty() { var instance = new SomeType { Items = new List<int>() }; // set the surrogate RuntimeTypeModel.Default.Add(typeof(SomeType), true).SetSurrogate(typeof(SomeTypeSurrogate)); // serialize-deserialize using cloning var clone = Serializer.DeepClone(instance); // clone is not null and empty Assert.IsNotNull(clone.Items); Assert.AreEqual(0, clone.Items.Count); } [ProtoContract] public class SomeType { [ProtoMember(1)] public List<int> Items { get; set; } } [ProtoContract] public class SomeTypeSurrogate { [ProtoMember(1)] public List<int> Items { get; set; } [ProtoMember(2)] public bool ItemsIsEmpty { get; set; } public static implicit operator SomeTypeSurrogate(SomeType value) { return value != null ? new SomeTypeSurrogate { Items = value.Items, ItemsIsEmpty = value.Items != null && value.Items.Count == 0 } : null; } public static implicit operator SomeType(SomeTypeSurrogate value) { return value != null ? new SomeType { Items = value.ItemsIsEmpty ? new List<int>() : value.Items } : null; } }


Haga sus tipos extensibles

protobuf-net sugiere la interfaz IExtensible, que le permite ampliar los tipos para que los campos se puedan agregar a un mensaje sin romper nada (lea más here ). Para usar la extensión protobuf-net, puede heredar la clase Extensible o implementar la interfaz IExtensible para evitar la restricción de herencia.
Ahora que su tipo es "extensible", define los [OnSerializing] y [OnDeserialized] para agregar los nuevos indicadores que se serializarán en la secuencia y se deserializarán a partir de ella al reconstruir el objeto con su estado original.
La ventaja es que no necesita definir nuevas propiedades ni nuevos tipos como sustitutos, los contras de que IExtensible no se admite si su tipo tiene subtipos definidos en su modelo de tipo.

[TestMethod] public void SerializeEmptyCollectionInExtensibleType_RemainEmpty() { var instance = new Store { Products = new List<string>() }; // serialize-deserialize using cloning var clone = Serializer.DeepClone(instance); // clone is not null and empty Assert.IsNotNull(clone.Products); Assert.AreEqual(0, clone.Products.Count); } [ProtoContract] public class Store : Extensible { [ProtoMember(1)] public List<string> Products { get; set; } [OnSerializing] public void OnDeserializing() { var productsListIsEmpty = this.Products != null && this.Products.Count == 0; Extensible.AppendValue(this, 101, productsListIsEmpty); } [OnDeserialized] public void OnDeserialized() { var productsListIsEmpty = Extensible.GetValue<bool>(this, 101); if (productsListIsEmpty) this.Products = new List<string>(); } }


El formato de conexión (definido por google, ¡no dentro de mi control!) Solo envía datos para los artículos . No hace distinción entre una lista vacía y una lista nula . Entonces, si no hay datos para enviar, sí, la longitud es 0 (es un formato muy frugal;

Los búferes de protocolo no incluyen ningún tipo de metadato en el cable.

Otro problema común aquí es que puede suponer que su propiedad de lista se instancia automáticamente como vacía, pero no lo será (a menos que su código lo haga, tal vez en un inicializador de campo o constructor).

Aquí hay un truco viable:

[ProtoContract] class SomeType { [ProtoMember(1)] public List<SomeOtherType> Items {get;set;} [DefaultValue(false), ProtoMember(2)] private bool IsEmptyList { get { return Items != null && Items.Count == 0; } set { if(value) {Items = new List<SomeOtherType>();}} } }

Hacky tal vez, pero debería funcionar. También puede perder el "conjunto de elementos" si lo desea y simplemente dejar caer el bool :

[ProtoMember(1)] public List<SomeOtherType> Items {get {return items;}} private readonly List<SomeOtherType> items = new List<SomeOtherType>(); [DefaultValue(false), ProtoMember(2)] private bool IsEmptyList { get { return items.Count == 0; } set { } }


public List<NotificationAddress> BccAddresses { get; set; }

puedes reemplazar con:

private List<NotificationAddress> _BccAddresses; public List<NotificationAddress> BccAddresses { get { return _BccAddresses; } set { _BccAddresses = (value != null && value.length) ? value : null; } }