C # inheritance of a generic collection and serialization

Setup:

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;} } } 

Now, trying to serialize using the following code snippet:

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

Looking at the resulting XML, I see that the value of ItemCollection.Name does not exist!

I think it might happen that the serializer sees the ItemCollection type as a simple collection, while ignoring any other added properties ...

Has anyone encountered such a problem and found a solution?

Hi,

StΓ©cy

+6
collections generics inheritance c # xml-serialization
source share
5 answers

This is Design By behavior. Upon receipt of the collection class, Xml Seralizier will serialize the collection items. To get around this, you must create a class that encapsulates the collection and name and will be serialized.

 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; } } } 

A detailed discussion is available here: http://blogs.vertigo.com/personal/chris/Blog/archive/2008/02/01/XML- Serialization- of a derivative collection.aspx

+12
source share

XmlSerializer is evil. However, any object that implements IEnumerable will be serialized as a simple collection, ignoring any additional properties that you added yourself.

You will need to create a new class containing both your property and the property that returns the collection.

+4
source share

I'm not sure if I am missing something, but you want the resulting xml to be

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

If so, just apply the XmlRoot attribute to the itemcollection class and give the element a name ...

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

This will instruct the serializer to display the required name for your collection container.

+2
source share

You can also try to implement your own serialization using the IXmlSerializable interface

  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 } 

Above code generates serialized xml as

 <?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
source share
 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; } } 
0
source share

All Articles