views:

2712

answers:

4

Hi,

The 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 fragment:

ItemCollection items = new ItemCollection();

...

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

Upon looking at the resulting XML I see that the ItemCollection.Name value is not there!

I think what may be happening is that the serializer sees the ItemCollection type as a simple Collection thus ignoring any other added properties...

Is there anyone having encountered such a problem and found a solution?

Regards,

Stécy

+3  A: 

XmlSerializer is evil. That said, any object that implements IEnumerable will be serialized as an simple collection, ignoring any extra properties you've added yourself.

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

Simon Svensson
+8  A: 

This behavior is "By Design". When deriving from a collection class the Xml Seralizier will only serialize the collection elements. To work around this you should create a class that encapsulates the collection and the name and have that 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-serializing-a-derived-collection.aspx

JaredPar
+1; note also that behaviour is shared by most data-binding frameworks too. It simply isn't a good idea for collections to have properties; collections have items (only) - that is their job.
Marc Gravell
Nice, now I need to wrap several collection-derived classes then...I am concerned that it would complicate the class diagram though...
Stecy
A: 

You can also try to implelemnt your own serialization using 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 will generate the 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"&gt;
    <Item>
      <Value>1</Value>
    </Item>
    <Item>
      <Value>2</Value>
    </Item>
  </ArrayOfItem>
</ItemCollection>
gk
That seems to be a good solution. You didn't specify code for the ReadXml method and I wonder if this would work as is. My guess is it would not?
Stecy
If you want to deserialize from xml at some time then you have to write the ReadXML other wise you can ignore that. This code works for serialization.
gk
A: 

I am not sure if I am missing something, but do 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 set the element name...

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

This will instruct the serializer to output the required name for you collection container.

NoelAdy