2010-03-04 10 views
27

tenemos algunos problemas con la serialización de una lista vacía. aquí algo de código en .NET CF usando 2,0protobuf-net: serializar una lista vacía

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

conseguimos una excepción, cuando tratamos de escribir en la corriente (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.

Respuesta

31

El formato de conexión (definido por google - ¡no dentro de mi control!) Solo envía datos para 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, quizás en un inicializador de campo o constructor).

Aquí un corte 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 podría perder el Items "set" si quieres 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 { } 
    } 
0
public List<NotificationAddress> BccAddresses { get; set; } 

se puede sustituir con:

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

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

Serialize Usando sustituto

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


hacer que sus tipos extensible

protobuf-net sugieren la interfaz IExtensible que le permiten ampliar los tipos para que los campos se pueden agregar a un mensaje sin nada rompiendo (leer 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", defina los métodos [OnSerializing] y [OnDeserialized] para agregar los nuevos indicadores que se serializarán en la secuencia y se deserializarán al reconstruir el objeto con su estado original.
La ventaja es que no necesita definir nuevas propiedades ni nuevos tipos como sustitutos, lo que contrasta es que IExtensible no es compatible 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>(); 
     } 
    } 
Cuestiones relacionadas