2009-03-20 10 views
6

générique La configuration:C# héritant de collecte et de sérialisation

class Item 
{ 
    private int _value; 

    public Item() 
    { 
     _value = 0; 
    } 

    public int Value { get { return _value; } set { _value = value; } } 
} 

class ItemCollection : Collection<Item> 
{ 
    private string _name; 

    public ItemCollection() 
    { 
     _name = string.Empty; 
    } 

    public string Name { get {return _name;} set {_name = value;} } 
} 

Maintenant, de sérialisation en utilisant le fragment de code suivant:

ItemCollection items = new ItemCollection(); 

... 

XmlSerializer serializer = new XmlSerializer(typeof(ItemCollection)); 
using (FileStream f = File.Create(fileName)) 
    serializer.Serialize(f, items); 

En regardant le XML résultant je vois que le ItemCollection.Name la valeur n'est pas là!

Je pense que ce qui peut se passer est que le sérialiseur voit le type ItemCollection comme une simple collection ignorant ainsi toutes les autres propriétés ajoutées ...

Est-ce qu'il ya quelqu'un ayant rencontré un tel problème et trouvé une solution?

Cordialement,

STECY

Répondre

12

Ce problème est "By Design". Lors de la dérivation d'une classe de collection, le Seralizier XML ne sérialisera que les éléments de la collection. Pour contourner ce problème, vous devez créer une classe qui encapsule la collection et le nom et qui est sérialisée.

class Wrapper 
{ 
    private Collection<Item> _items; 
    private string _name; 

    public Collection<Item> Items { get {return _items; } set { _items = value; } } 
    public string Name { get { return _name; } set { _name = value; } } 
} 

Une discussion détaillée est disponible ici: http://blogs.vertigo.com/personal/chris/Blog/archive/2008/02/01/xml-serializing-a-derived-collection.aspx

+0

+1; Notez également que le comportement est partagé par la plupart des frameworks de liaison de données. Ce n'est simplement pas une bonne idée que les collections aient des propriétés; collections ont des objets (seulement) - c'est leur travail. –

+0

Nice, maintenant je dois emballer plusieurs classes dérivées de la collection puis ... Je suis préoccupé par le fait que cela compliquerait le diagramme de classe ... –

4

XmlSerializer est le mal. Cela dit, tout objet qui implémente IEnumerable sera sérialisé comme une collection simple, en ignorant les propriétés supplémentaires que vous avez ajouté vous-même.

Vous devrez créer une nouvelle classe qui contient à la fois votre propriété et une propriété qui renvoie la collection.

0

Vous pouvez aussi implelemnt votre propre sérialisation en utilisant l'interface IXmlSerializable

public class ItemCollection : Collection<Item>,IXmlSerializable 
    { 
     private string _name; 

     public ItemCollection() 
     { 
      _name = string.Empty; 
     } 

     public string Name 
     { 
      get { return _name; } 
      set { _name = value; } 
     } 

#region IXmlSerializable Members 

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

     public void ReadXml(System.Xml.XmlReader reader) 
     { 

     } 

     public void WriteXml(System.Xml.XmlWriter writer) 
     { 
       writer.WriteElementString("name", _name); 
       List<Item> coll = new List<Item>(this.Items); 
       XmlSerializer serializer = new XmlSerializer(coll.GetType()); 
       serializer.Serialize(writer, coll); 

     } 

#endregion 
    } 

code ci-dessus va générer le code XML sérialisé

<?xml version="1.0"?> 
<ItemCollection> 
    <name /> 
    <ArrayOfItem xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <Item> 
     <Value>1</Value> 
    </Item> 
    <Item> 
     <Value>2</Value> 
    </Item> 
    </ArrayOfItem> 
</ItemCollection> 
+0

Cela semble être une bonne solution. Vous n'avez pas spécifié de code pour la méthode ReadXml et je me demande si cela fonctionnerait tel quel. Je suppose que ce ne serait pas? –

+0

Si vous voulez désérialiser à partir de xml à un moment donné, vous devez écrire le ReadXML autrement vous pouvez l'ignorer. Ce code fonctionne pour la sérialisation. –

2

Je ne sais pas s'il me manque quelque chose ng, mais vous voulez que le XML résultant soit

<ItemCollection> 
    <Name>name val</Name> 
    <Item> 
     <Value>1</alue> 
    </Item 
    <Item> 
     <Value>2</alue> 
    </Item 
</ItemCollection> 

Si oui, il suffit d'appliquer l'attribut XmlRoot à la classe ItemCollection et définissez le nom de l'élément ...

[XmlRoot(ElementName="ItemCollection")] 
public class ItemCollection : Collection<Item> 
{ 
    [XmlElement(ElementName="Name")] 
    public string Name {get;set;} 
} 

Cela commandera sérialiseur pour afficher le nom requis pour votre conteneur de collection.

0
public class Animals : List<Animal>, IXmlSerializable 
{ 
    private static Type[] _animalTypes;//for IXmlSerializable 
    public Animals() 
    { 
     _animalTypes = GetAnimalTypes().ToArray();//for IXmlSerializable 
    } 

    // this static make you access to the same Animals instance in any other class. 
    private static Animals _animals = new Animals(); 
    public static Animals animals 
    { 
     get {return _animals; } 
     set { _animals = value; } 
    } 

    #region IXmlSerializable Members 

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

    public void ReadXml(System.Xml.XmlReader reader) 
    { 
     bool wasEmpty = reader.IsEmptyElement; 
     reader.Read(); 
     if (wasEmpty) 
      return; 

     reader.MoveToContent(); 
     reader.ReadStartElement("Animals"); 
     // you MUST deserialize with 'List<Animal>', if Animals class has no 'List<Animal>' fields but has been derived from 'List<Animal>'. 
     List<Animal> coll = GenericSerializer.Deserialize<List<Animal>>(reader, _animalTypes); 
     // And then, You can set 'Animals' to 'List<Animal>'. 
     _animals.AddRange(coll); 
     reader.ReadEndElement(); 

     //Read Closing Element 
     reader.ReadEndElement(); 
    } 

    public void WriteXml(System.Xml.XmlWriter writer) 
    { 
     writer.WriteStartElement("Animals"); 
     // You change 'List<Animal>' to 'Animals' at first. 
     List<Animal> coll = new List<Animal>(_animals); 
     // And then, You can serialize 'Animals' with 'List<Animal>'. 
     GenericSerializer.Serialize<List<Animal>>(coll, writer, _animalTypes); 
     writer.WriteEndElement(); 
    } 

    #endregion 

    public static List<Type> GetAnimalTypes() 
    { 
     List<Type> types = new List<Type>(); 
     Assembly asm = typeof(Animals).Assembly; 
     Type tAnimal = typeof(Animal); 

     //Query our types. We could also load any other assemblies and 
     //query them for any types that inherit from Animal 
     foreach (Type currType in asm.GetTypes()) 
     { 
      if (!currType.IsAbstract 
       && !currType.IsInterface 
       && tAnimal.IsAssignableFrom(currType)) 
       types.Add(currType); 
     } 

     return types; 
    } 
} 
Questions connexes