2010-03-04 13 views
27

. Biz akışı (bytes.length aralık dışı) üzerine yazmaya çalışacak bir istisna var protobuf-net: Boş bir listenin serileştirilmesiyle ilgili bazı sorunlarımız var demektir boş bir liste

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

CF 2.0

kullanarak .NET burada bazı kodu. Ancak boş bir liste içeren bir tür 0 bayt, sağ (tip bilgisi?) Olmamalıdır?

Bu tür bir gönderiye ihtiyacımız var çünkü yanıtta, İstemciden Sunucu'nun iletileri var.

cevap

31

tel biçimi (google tarafından tanımlanan - değil benim kontrolüm içinde) sadece öğeler için veri gönderir. boş listesi ve null listesi arasında hiçbir ayrım yapmaz. Yani gönderilecek veri yoksa - evet, uzunluk 0'dır (çok tutumlu bir biçimdir; -p).

Protokol tamponları, tel üzerinde herhangi bir tür meta veri içermez.

Diğer bir genel özellik ise, liste özelliğinizin otomatik olarak boş olarak doldurulduğunu varsaymanız olabilir, ancak kodunuz değişmediği sürece (alan başlatıcı veya yapıcıda). belki Hacky

[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>();}} 
    } 
} 

, ancak çalışması gerekir:

İşte çalışılabilir bir beygir. Ayrıca isterseniz Items "set" kaybeder ve sadece bool düşebilir:

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

sizinle değiştirebilirsiniz: @Marc söylediği gibi

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

, tel biçim yalnızca gönderir öğeler için veri, yani listenin boş mu yoksa boş mu olduğunu bilmek için, o bilgi akışını akışa eklemelisiniz. Orijinal koleksiyon boş olup olmadığını göstermek için ekstra özellik ekleme
kolaydır ama orijinal tip tanımını değiştirmek istemiyorsanız başka iki seçenek vardır: Vekil

vekil türünün kullanılması

serialize ekstra özelliğe sahip olacak (orijinal türünüz el değmemiş durumda olacak) ve listenin orijinal durumunu geri yükleyecektir: null, öğelerle veya boş.

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


Kişisel Türleri Genişletilebilir olun

protobuf-net alanlar kırma şey olmadan bir mesaj eklenebilir böylece türlerini uzatmak için izin IExtensible arayüzü önermek (here devamı) . Protobuf-net uzantısını kullanabilmek için, Extensible sınıfını miras alabilir veya miras kısıtlamasını önlemek için IExtensible arabirimini uygulayabilirsiniz.
Şimdi türünüzün "genişletilebilir" olduğunu düşünüyorsanız, [OnSerializing] ve [OnDeserialized] tanımlarını kullanarak, akışa serileştirilecek yeni göstergelerin eklenmesi ve nesneyi orijinal durumuyla yeniden yapılandırırken bunlardan serileştirilmesi. Artıları, yeni özellikler veya yeni türler için vekil olarak tanımlamanız gerekmediği için, türünüzün tür modelinizde tanımlanmış alt türleri varsa, IExtensible'un desteklenmemesidir.

[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>(); 
     } 
    } 
İlgili konular