19

I've got a simple class that inherits from Collection and adds a couple of properties. I need to serialize this class to XML, but the XMLSerializer ignores my additional properties.

I assume this is because of the special treatment that XMLSerializer gives ICollection and IEnumerable objects. What's the best way around this?

Here's some sample code:

using System.Collections.ObjectModel;
using System.IO;
using System.Xml.Serialization;

namespace SerialiseCollection
{
    class Program
    {
        static void Main(string[] args)
        {
            var c = new MyCollection();
            c.Add("Hello");
            c.Add("Goodbye");

            var serializer = new XmlSerializer(typeof(MyCollection));
            using (var writer = new StreamWriter("test.xml"))
                serializer.Serialize(writer, c);
        }
    }

    [XmlRoot("MyCollection")]
    public class MyCollection : Collection<string>
    {
        [XmlAttribute()]
        public string MyAttribute { get; set; }

        public MyCollection()
        {
            this.MyAttribute = "SerializeThis";
        }
    }
}

This outputs the following XML (note MyAttribute is missing in the MyCollection element):

<?xml version="1.0" encoding="utf-8"?>
<MyCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <string>Hello</string>
    <string>Goodbye</string>
</MyCollection>

What I want is

<MyCollection MyAttribute="SerializeThis" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <string>Hello</string>
    <string>Goodbye</string>
</MyCollection>

Any ideas? The simpler the better. Thanks.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
roomaroo
  • 5,831
  • 4
  • 31
  • 31

5 Answers5

16

Collections generally don't make good places for extra properties. Both during serialization and in data-binding, they will be ignored if the item looks like a collection (IList, IEnumerable, etc - depending on the scenario).

If it was me, I would encapsulate the collection - i.e.

[Serializable]
public class MyCollectionWrapper {
    [XmlAttribute]
    public string SomeProp {get;set;} // custom props etc
    [XmlAttribute]
    public int SomeOtherProp {get;set;} // custom props etc
    public Collection<string> Items {get;set;} // the items
}

The other option is to implement IXmlSerializable (quite a lot of work), but that still won't work for data-binding etc. Basically, this isn't the expected usage.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
14

If you do encapsulate, as Marc Gravell suggests, the beginning of this post explains how to get your XML to look exactly like you describe.

http://blogs.msdn.com/youssefm/archive/2009/06/12/customizing-the-xml-for-collections-with-xmlserializer-and-datacontractserializer.aspx

That is, instead of this:

<MyCollection MyAttribute="SerializeThis" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Items>
    <string>Hello</string>
    <string>Goodbye</string>
  <Items>
</MyCollection>

You can have this:

<MyCollection MyAttribute="SerializeThis" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
  xmlns:xsd="http://www.w3.org/2001/XMLSchema">      
  <string>Hello</string>
  <string>Goodbye</string>
</MyCollection>
Neil
  • 7,227
  • 5
  • 42
  • 43
  • @neilwhitaker1: I upvoted this answer, but it would be even better if you described the technique in the answer rather than making us go read the blog post. You can still reference the post. – John Saunders Mar 25 '10 at 19:19
  • 1
    The blog post answers the question very well with an appropriate level of detail, and the answer is easy to find within the blog post. I think that linking to the blog in this case is preferable. – AndrewS Dec 25 '10 at 12:24
  • 1
    @AndrewS That is true, until ten years later and the link is broken and you have to dig it up: https://learn.microsoft.com/en-us/archive/blogs/youssefm/customizing-the-xml-for-collections-with-xmlserializer-and-datacontractserializer – Nate Mar 17 '21 at 05:41
3

As Neil Whitaker suggests and in case his link dies..

Create an inner collection to store the strings and apply the XmlElement attribute to mask the collection name. Produces the same xml output as if MyCollection inherited from Collection, but also serializes attributes on the parent element.

[XmlRoot("MyCollection")]
public class MyCollection 
{
    [XmlAttribute()]
    public string MyAttribute { get; set; }

    [XmlElement("string")]
    public Collection<string> unserializedCollectionName { get; set; }

    public MyCollection()
    {
        this.MyAttribute = "SerializeThis";
        this.unserializedCollectionName = new Collection<string>();
        this.unserializedCollectionName.Add("Hello");
        this.unserializedCollectionName.Add("Goodbye");
    }
}
Trevor Daniels
  • 1,119
  • 8
  • 5
0

Sometimes you just want to do what you just want to do; the Framework be damned.

I posted an answer here Property of a List<T> Not Deserialized

that does what the OP wanted to do.

Community
  • 1
  • 1
toddmo
  • 20,682
  • 14
  • 97
  • 107
0

I've been fighting with the same issue Romaroo is (wanting to add properties to the xml serialization of a class that implements ICollection). I have not found any way to expose properties that are in the collection class. I even tried using the XmlAttribute tag and making my properties show up as attributes of the root node, but no luck there either. I was however able to use the XmlRoot tag on my class to rename it from "ArrayOf...". Here are some references in case you're interested:

VanOrman
  • 4,351
  • 5
  • 35
  • 31