2017-03-02 1 views
0

Je travaille sur le projet écrit sur C# avec protobuf-net. J'ai un code sérialisant/désérialisant Dictionary<MyClass1, MyClass2> dans le fichier. En outre, j'ai un fichier avec des données sérialisées. Lorsque je tente de le désérialiser, je reçois une exception key ne peut pas être null. Je ne comprends pas comment est-il possible puisque Dictionary ne permet pas null, il semble que je ne pouvais pas sérialiser un tel dictionnaire. Je pense que le fichier a été corrompu, mais je ne suis pas sûr. J'ai essayé de le déboguer, et semble que quelques clés et valeurs ont été désérialisées correctement, mais au milieu du processus de désérialisation, la clé null s'est produite et je vois l'exception. J'ai essayé d'utiliser substitut pour ProductName comme mentionné here mais cela n'aide pas.Protobuf dictionnaire désérialisation jette une exception de clé nulle

Comment désérialiser ce fichier? Peut-il y avoir un moyen de désérialiser certains objets au lieu de null pour ProductName?

Exception:

System.ArgumentNullException: La valeur ne peut être nulle. Nom du paramètre: touche à System.Collections.Generic.Dictionary 2.Insert(TKey key, TValue value, Boolean add) at System.Collections.Generic.Dictionary 2.System.Collections.Generic.ICollection> .Add (KeyValuePair`2 KeyValuePair) à proto_10 (Object, ProtoReader) à ProtoBuf.Meta.TypeModel.DeserializeCore (Lecteur ProtoReader, Type de type, Valeur d'objet, Boolean noAutoCreate) dans c: \ Dev \ protobuf-net \ protobuf-net \ Meta \ TypeModel.cs: ligne 704 à ProtoBuf.Meta.TypeModel.Deserialize (Source de flux, Valeur d'objet , Type Type, contexte SerializationContext) dans c: \ Dev \ protobuf-net \ protobuf-net \ Meta \ TypeModel.cs: ligne 588 à ProtoBuf.Serializer.Deserialize [T] (source de flux) dans c: \ Dev \ protobuf -net \ protobuf-net \ Serializer.cs: ligne 77

code:

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

Répondre

0

En premier lieu, pour vérifier si le fichier est en fait corrompu, vous pouvez essayer de suivre les instructions Recovering corrupted file serialize with Protobuf-net pour vérifier si le fichier est corrompu.

Ensuite, indépendamment du fait que le fichier est corrompu ou non, désérialiser dans la mesure du possible, vous pouvez utiliser Serializer.Merge<T>(Stream source, T instance) pour fusionner le fichier sur un pré-alloué ProductNameIndex, comme ceci:

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

index devrait contient maintenant autant d'entrées qu'il était possible de désérialiser complètement.

Maintenant, si le fichier est corrompu, qui est probablement le meilleur que vous pouvez faire à moins que vous voulez charger dans un MemoryStream et tenter de corriger manuellement octet par octet. Mais si le fichier n'est pas corrompu, vous devez déboguer pour savoir quel est le problème. Une action à effectuer consiste à comparer le contrat généré pour ProductNameIndex avec le contrat réellement utilisé dans le fichier. Pour voir le contrat pour ProductNameIndex, vous pouvez faire:

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

Ensuite, comparez la sortie de Recovering corrupted file serialize with Protobuf-net, que je crois décharges Seuls les champs de haut niveau.Si les numéros de champs et les types de fils ne correspondent pas, vous savez que vous lisez un fichier dont l'objet racine n'est pas réellement ProductNameIndex. Vous pouvez également utiliser l'infortmation de contrat du GetSchema() pour entrer manuellement dans des champs (chaînes) et des objets imbriqués répétés.

Enfin, si les contrats correspondent, mais en quelque sorte le système émetteur émet un message KeyValuePair_ProductName_ProductId avec un optional ProductName Key manquant, vous pouvez modifier votre ProductNameIndex d'ignorer ces entrées non valides en utilisant une propriété de tableau de substitution:

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

Ou , si vous ne pouvez pas ajouter d'attributs protobuf à votre type, vous pouvez introduire un type de substitution pour ProductNameIndex avec les vérifications nécessaires, comme expliqué here.

Enfin, jetez un oeil à Using Protobuf-net, I suddenly got an exception about an unknown wire-type qui a plusieurs suggestions utiles pour le diagnostic des problèmes de protobuf-net, y compris:

Le le plus probable cause (dans mon expérience) est que vous avez remplacé un fichier existant , mais ne l'a pas tronqué; c'est-à-dire que c'était 200 octets; vous l'avez réécrit, mais avec seulement 182 octets. Il y a maintenant 18 octets de déchets à la fin de votre flux qui le font trébucher. Les fichiers doivent être tronqués lors de la réécriture des tampons de protocole.