XmlSerializer Misconceptions Part 1
I figure one of the larger areas of confusion and trouble is around XML serialization. Lots of people don’t realize the power that can be had or get tripped up by little problems, so hopefully I can do a few of these and try to show some cool stuff that can be done.
The first big misconception about the XmlSerializer is the idea that it cannot handle generic lists with derived classes. I’ve been told by people that the XML doesn’t contain type information and won’t work, or that they get an error while trying to serialize. The funny part is that it would only take one little line of code to fix it.
So first, let’s set up the issue. Here’s a bunch of code to show a basic example of XML serialization:
public class Root
{
public float Foo { get; set; }
}
public class Derived : Root
{
public bool Bar { get; set; }
}
public class MainObject
{
public List<Root> Stuff { get; set; }
public MainObject()
{
Stuff = new List<Root>();
}
}
class Program
{
static void Main(string[] args)
{
MainObject obj = new MainObject();
for (int i = 0; i < 10; i++)
{
obj.Stuff.Add(new Derived
{
Bar = i % 2 == 0,
Foo = (float)Math.Sin(i)
});
}
XmlSerializer serializer = new XmlSerializer(typeof(MainObject));
string path = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.Desktop),
"Test.xml");
using (StreamWriter writer = new StreamWriter(path))
serializer.Serialize(writer, obj);
}
}
If you were to run this code, you will get an InvalidOperationException on the serializer.Serialize method call. To most people this means that obviously we can’t use the Derived type in the list because the XmlSerializer can’t handle it. But rather than assume that, let’s just fix our problem by attaching an attribute to the MainObject class:
[XmlInclude(typeof(Derived))]
public class MainObject
{...}
By doing that, we’ve now instructed the serializer to include the reflection information for that type when serializing so it can be used. If we run this again, you’ll find an XML document on your desktop filled with the information. Contrary to what some may say, you’ll see each item in the list has an xsi:type attribute added onto it so that the serializer will know what type to create when you later deserialize the list.
This error occurs because the XmlSerializer cannot possibly know every subclass of our Root class. It would have to search all currently loaded assemblies and that’s just not an efficient way to go about things. So instead the serializer requires that you manually include the types that might be in use in your object. You can have as many of the XmlInclude attributes as you need, so just keep adding them on for every new subclass you have.
Possibly Related Posts
(Automatically Generated)IDictionary + XmlSerializer = Epic FAIL
Creating Your Own XNB Files
.NET Misconceptions Part 1
Storage Device Management 2.0
My Singleton Base Class

You know something I’ve yet to find a satisifactory answer too, is that I would expect XmlSerializer to be isomorphic but it does not appear to be. Basically the [DefaultValue(true)] is used during serialisation but not deserialastion. Of course that means something just written to a stream and read back in to another object has different values.
namespace Noddy2
{
#region Class to be serialised
[Serializable()]
public class Clue
{
[DefaultValue("Nothing To See Here")] //Comment out and it won’t assert
public string seen;//Set “Nothing To See Here” here to to work around
};
#endregion
class Program
{
static void Main(string[] args)
{
Clue clue = new Clue();
clue.seen = “Nothing To See Here”;
//Serialise the clue…
StringBuilder sb = new StringBuilder();
XmlSerializer xs = new XmlSerializer(typeof(Clue));
using (StringWriter sw = new StringWriter(sb))
{
xs.Serialize(sw, clue);
}
string serialised = sb.ToString();
//Show serialised version of the clue…
Console.WriteLine(“Clue1:”);
Console.WriteLine(serialised);
//Deserialise the clue…
Clue clue2;
using (StringReader sr = new StringReader(serialised))
{
clue2 = (Clue)xs.Deserialize(sr);
}
Debug.Assert(clue.seen == clue2.seen, “Clues don’t match”);
//Show clue 2 on console
sb.Length = 0;
using (StringWriter sw = new StringWriter(sb))
{
xs.Serialize(sw, clue2);
Console.WriteLine(“Clue2:”);
Console.WriteLine(sb.ToString());
}
}
}
}