2017-03-02 79 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个字节的垃圾被绊倒。重写协议缓冲区时,文件必须被截断。