2017-03-02 39 views
0

我正在編寫C#protobuf-net編寫的項目。我有代碼序列化/反序列化Dictionary<MyClass1, MyClass2>文件。另外,我有一個帶有序列化數據的文件。當我嘗試反序列化它時,我收到一個異常key不能是null。我不明白怎麼可能,因爲Dictionary不允許null,它看起來像我不能序列化這樣的字典。我認爲該文件已損壞,但我不確定。我試圖調試它,看起來像幾個鍵和值正確反序列化,但在反序列化過程中,null鍵發生,我看到異常。我試圖使用代理ProductName提到here但它沒有幫助。Protobuf詞典diserialization引發null key異常

如何反序列化此文件? 可能有反序列化某些對象的方法,而不是null對於ProductName

例外:

System.ArgumentNullException:值不能爲空。 參數名稱:在System.Collections.Generic.Dictionary 2.Insert(TKey key, TValue value, Boolean add) at System.Collections.Generic.Dictionary 2.System.Collections.Generic.ICollection鍵 >。新增(KeyValuePair`2 keyValuePair) 在proto_10(對象,ProtoReader) 在ProtoBuf.Meta.TypeModel.DeserializeCore (ProtoReader reader,Type type,Object value,Boolean noAutoCreate)in ProtoBuf.Meta.Type.Model.Deserialize(Stream source,Object value)在c:\ Dev \ protobuf-net \ protobuf-net \ Meta \ TypeModel.cs中:行704 ,Type type,SerializationContext context)在c:\ Dev \ protobuf-net \ protobuf-net \ Meta \ TypeModel.cs中:第588行 位於ProtoBuf.Serializer.Deserialize [T](Stream source)in c:\ Dev \ protobuf -net \ protobuf-net \ Serializer.cs:line 77

代碼:

[ DataContract ] 
    public sealed class ProductName 
    { 
     [ DataMember(Order = 1) ] 
     public string Name { get; private set; } 

     public static readonly ProductName Undefined = Create("Unknown"); 

     private ProductName() 
     { 
     } 

     private ProductName(string Name) 
     { 
      this.Name = Name.Trim(); 
     } 

     public static ProductName Create(string Name) 
     { 
      Condition.Requires(Name, "Name").IsNotNullOrEmpty(); 

      return new ProductName(Name); 
     } 

     public static ProductName TryCreate(string Name) 
     { 
      return Name.IsValidName() ? new ProductName(Name) : null; 
     } 

     public override string ToString() 
     { 
      return this.Name; 
     } 

     public override int GetHashCode() 
     { 
       var stableHashCodeIgnoringCase = this.Name.GetStableHashCodeIgnoringCase(); 
       return stableHashCodeIgnoringCase; 
     } 

     #region Equality members 
     public bool Equals(ProductName other) 
     { 
      if(ReferenceEquals(null, other)) 
       return false; 
      if(ReferenceEquals(this, other)) 
       return true; 
      return string.Equals(this.Name, other.Name, StringComparison.InvariantCultureIgnoreCase); 
     } 

     public override bool Equals(object obj) 
     { 
      if(ReferenceEquals(null, obj)) 
       return false; 
      if(ReferenceEquals(this, obj)) 
       return true; 
      if(obj.GetType() != this.GetType()) 
       return false; 
      return this.Equals((ProductName)obj); 
     } 
     #endregion 
    } 


    [ DataContract ] 
    public class ProductNameIndex 
    { 
     [ DataMember(Order = 1) ] 
     public IDictionary< ProductName, ProductId > Products{ get; private set; } 

     public ProductNameIndex() 
     { 
      this.Products = new Dictionary< ProductName, ProductId >(); 
     }  
    } 

回答

0

首先,對文件進行測試實際上是否損壞,可以嘗試按照Recovering corrupted file serialize with Protobuf-net說明檢查,看是否該文件已損壞。

接下來,不管文件是否已損壞或沒有,反序列化儘可能,你可以使用Serializer.Merge<T>(Stream source, T instance)的文件合併到一個預先分配ProductNameIndex,像這樣:

var index = new ProductNameIndex(); 
try 
{ 
    Serializer.Merge(stream, index); 
} 
catch (Exception ex) 
{ 
    // Log the error 
    Debug.WriteLine(ex); 
} 

index應現在包含儘可能多的條目,因爲它可能完全反序列化。

現在,如果文件損壞,這可能是,除非你想將其加載到一個MemoryStream,並嘗試手動修復它,你能做的最好的逐字節。但如果文件不是已損壞,則需要進行調試以找出問題所在。要執行的一個操作是將ProductNameIndex生成的合同與文件中實際使用的合同進行比較。要查看ProductNameIndex合同,你可以這樣做:

Debug.WriteLine(ProtoBuf.Meta.RuntimeTypeModel.Default.GetSchema(typeof(ProductNameIndex))); 

然後輸出Recovering corrupted file serialize with Protobuf-net比較,我相信轉儲頂級域。如果字段號和線類型不匹配,則會知道您正在讀取的文件的根對象實際上不是ProductNameIndex。您還可以使用GetSchema()中的合同資料庫手動分步重複字段(字符串)和嵌套對象。

最後,如果合同相符,但不知何故發送系統正在發射KeyValuePair_ProductName_ProductId消息缺少optional ProductName Key,你可以修改你的ProductNameIndex使用代理數組屬性會忽略掉這些無效項:

[DataContract] 
[ProtoContract] 
public class ProductNameIndex 
{ 
    [ProtoMember(1, Name = "Products")] 
    KeyValuePair<ProductName, ProductId>[] ProductsSurrogateArray 
    { 
     get 
     { 
      return Products.ToArray(); 
     } 
     set 
     { 
      if (value == null) 
       return; 
      foreach (var p in value) 
      { 
       if (p.Key == null) 
        Debug.WriteLine("Ignoring invalid null key"); 
       else 
        Products.Add(p); 
      } 
     } 
    } 

    [ProtoIgnore] 
    [DataMember(Order = 1)] 
    public IDictionary<ProductName, ProductId> Products { get; private set; } 

    public ProductNameIndex() 
    { 
     this.Products = new Dictionary<ProductName, ProductId>(); 
    } 
} 

或者,如果您無法將protobuf屬性添加到您的類型中,則可以使用必要的檢查爲ProductNameIndex引入代理類型,如here所述。

最後,來看看Using Protobuf-net, I suddenly got an exception about an unknown wire-type這對於診斷protobuf網的問題,包括一些有用的建議:

最有可能原因(在我的經驗)是,已覆蓋現有文件,但沒有截斷它;即它是200字節;你已經重寫了它,但只有182字節。現在,流的末尾有18個字節的垃圾被絆倒。重寫協議緩衝區時,文件必須被截斷。