2010-03-20 7 views
3

Voici mes classes: http://pastebin.com/3dc5Vb1tLinkedList ne peut pas être sérialisé?

Lorsque je tente de lancer

BookStore b = new BookStore(); 
b.LoadFromXML(Server.MapPath("list.xml")); 
Label1.Text = b.ToString(); 

Je reçois l'erreur suivante:

You must implement a default accessor on System.Collections.Generic.LinkedList`1[[Book, App_Code.cxsacizw, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]] because it inherits from ICollection.

La source d'erreur est XmlSerializer s = new XmlSerializer(typeof(BookStore));

Lorsque j'ai essayé de chercher une solution sur google, j'ai trouvé que LinkedList a quelques problèmes avec la sérialisation. Comment puis-je y faire face?

Merci beaucoup.

+0

double possible de [Comment Xml sérialiser une LinkedList?] (Http://stackoverflow.com/questions/2271582/how-to-xml-serialize-a-linkedlist) – nawfal

Répondre

5

Il semble que ce ne soit pas possible.

Rapport de bogue ici: linkedlist-t-can-not-be-serialized-using-the-xmlserializer. Où lire ce qui suit:

Posted by Microsoft on 11/11/2004 at 19:35
We chose not to have a indexer method on LinkedList for performance reason. So LinkedList will not be XMLSeriliazable.

+0

Si oui , Que puis-je faire? Je ne veux pas utiliser un tableau, car je ne suis pas sûr du nombre d'éléments que comprendra le tableau. – iTayb

+1

Vous utilisez une liste http://msdn.microsoft.com/fr-fr/library/6sh2ey19.aspx – kervin

4

Dans l'abstrait:

Certains types .Net comme les listes chaînées, hachage tables (dictionnaires), etc ont des problèmes lorsque vous essayez de sérialisation. Cela semble être principalement de conception: il existe d'autres types, plus simples, qui peuvent représenter la même plage de valeurs (par exemple, une liste normale au lieu d'une liste liée, ou une liste de paires au lieu d'un dictionnaire). .Net suppose que si vous utilisez le type plus spécifique, vous voulez ses caractéristiques spécifiques. Lorsque de telles fonctionnalités ne peuvent pas être sérialisées (par exemple, une table de hachage ne peut pas être décrite comme telle en XML), les problèmes commencent.

Le point clé est: avez-vous vraiment besoin des fonctionnalités spécifiques de ces types sur leur forme sérialisée? Par exemple, si vous devez sérialiser une liste chaînée pour que la version sérialisée inclue les liens entre les éléments, alors vous allez avoir de sérieux problèmes. Heureusement, dans la plupart des cas, vous n'aurez besoin des fonctionnalités spéciales que lorsque vous travaillez réellement avec l'objet, vous pouvez donc sérialiser une version simplifiée (mais assez complète) et reconstruire l'objet avancé lors de la désérialisation.

Pour rendre cela possible, .Net inclut des outils utiles pour se mêler du processus de/sérialisation. Tout d'abord, vous devez toujours marquer vos objets sérialisables en tant que tels avec System.SerializableAttribute (http://msdn.microsoft.com/en-us/library/system.serializableattribute.aspx). Ensuite, vous pouvez implémenter System.Runtime.Serialization.ISerializable (http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializable.aspx) pour avoir un contrôle total sur le processus de sérialisation. Dans le cas le plus simple, tout ce que vous devez faire est de convertir votre liste liée en une liste normale et l'ajouter à l'argument SerializationInfo que vous obtenez dans GetObjectData (...) comme une seule valeur (je suppose que vous l'étiez comme " value ") pour la sérialisation. Ensuite, pour activer la désérialisation, ajoutez un constructeur comme l'exemple ci-dessous.

Toutefois, cela ne concerne que l'infrastructure de sérialisation partagée. Pour obtenir un contrôle total avec la sérialisation XML, vous devez implémenter System.Xml.Serialization.IXmlSerializable. Dans ce cas, gardez à l'esprit que l'auteur encapsulera implicitement votre sortie dans un élément qui indique le type d'objet en cours de sérialisation; et le lecteur doit creuser explicitement à l'intérieur de cet élément (dans certains cas, cette asymétrie peut être nécessaire). Il peut être difficile d'implémenter cette interface si vous n'y êtes pas habitué.Les flux XML de Net; mais heureusement pour vous j'ai dû faire quelque chose de similaire avec les dictionnaires il y a un moment et je pourrais recycler la plupart du code;).

Cet exemple fournit l'essentiel pour une LinkedList qui est sérialisée en tant que liste "normale", et désérialise de nouveau vers une liste chaînée. Le formulaire sérialisé et non contient les liens inter-éléments; mais ces liens sont refaits de manière fiable lors de la désérialisation.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Xml.Serialization; 
using System.Runtime.Serialization; 
using System.Xml; 
using System.IO; 

namespace WFTest { 
    [Serializable] 
    class SerializableLinkedList<T>: LinkedList<T>, ISerializable, IXmlSerializable { 
     void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { 
      info.AddValue("value", this.ToList()); 
     } 
     // Implied by ISerializable, but interfaces can't actually define constructors: 
     SerializableLinkedList(SerializationInfo info, StreamingContext context) 
      : base((IEnumerable<T>)info.GetValue("value", typeof(List<T>))) { } 

     System.Xml.Schema.XmlSchema IXmlSerializable.GetSchema() { return null; } 

     void IXmlSerializable.ReadXml(XmlReader reader) { 
      this.Clear(); // Start with an empty list 
      reader.ReadStartElement(); // Skips the opening tag 
      while (reader.LocalName=="item") { // Retrieve all elements: 
       T value; 
       if(reader.IsEmptyElement) { // If element is empty... 
        value=default(T); // the item's value falls back to default(T) 
        reader.ReadStartElement(); // and consume the (empty) element 
       } else { 
        // IIRC, ReadInnerXml() consumes the outer tag, despite not returning them. 
        value=(T)((new XmlSerializer(typeof(T))).Deserialize(new StringReader(reader.ReadInnerXml()))); 
       } 
       this.AddLast(value); 
      } 
      reader.ReadEndElement(); // Consumes the remaining closing tag from the reader 
     } 

     void IXmlSerializable.WriteXml(XmlWriter writer) { 
      foreach(T item in this) { 
       // Format the item itself: 
       StringBuilder sb=new StringBuilder(); 
       (new XmlSerializer(typeof(T))).Serialize(XmlWriter.Create(sb), item); 
       XmlDocument doc=new XmlDocument(); 
       doc.LoadXml(sb.ToString()); 
       // and now write it to the stream within <item>...</item> tags 
       writer.WriteStartElement("item"); 
       writer.WriteRaw(doc.DocumentElement.OuterXml); 
       writer.WriteEndElement(); // </item> 
      } 
     } 
    } 
} 

Utilisez cette classe au lieu de la classe LinkedList « brute » pour vos objets (ou une classe de base si vous avez besoin de tirer de LinkedList) et sérialisation ne devrait pas déclencher des plus de problèmes avec les listes. Notez, cependant, que tout ce que vous utilisez comme paramètre "T" pour cette liste doit être lui-même sérialisable, mais il n'y a aucun moyen d'imposer une telle exigence dans le code. En aparté, permettez-moi de traiter certaines choses légales: en tant qu'auteur de l'extrait de code ci-dessus, j'accorde une autorisation mondiale, non révocable, non exclusive, à quiconque de l'utiliser à des fins quelconques (y compris, mais ne pas se limiter à, créer des œuvres dérivées de toute nature, et les distribuer sur n'importe quel formulaire). L'attribution n'est pas requise, mais elle est toujours la bienvenue. Oh, et après avoir regardé votre code, je vous encourage fortement à utiliser StringBuilder pour votre implémentation de la méthode ToString(): chaque fois que votre code appelle + = sur une chaîne, un nouvel objet chaîne est créé (en prenant temps et mémoire). Bien qu'il soit peu probable que vous manquiez de mémoire, une liste considérablement longue peut facilement avoir un impact sur les performances de votre application.

Hope this helps

Questions connexes