Tutorial: Xml Serialization, Part 2

Posted by Matt | Filed under , , ,

Project: XmlSerialization.zip (11.03 kb)

In part 1, we introduced Xml Serialization.  In that post, we created a Family class, added some family members to it and serialized it to an Xml file.

But what if we need to specialize the family members?  What if we want a special Father class and Mother classes?  This can be done too:

[Serializable]
public class Mother : FamilyMember
{
  public override short Age
  {
    get { return 29; }
    set { /* never ages */ }
  }
}

[Serializable]
public class Father : FamilyMember
{
  public short GolfHandicap { get; set; }
}

If we construct our family:

Family f = new Family();
f.Name = "Smith";
f.Members.Add(new Father() { Name = "John", 
    Age = 50, GolfHandicap = 12 });
f.Members.Add(new Mother() { Name = "Mary", 
    Age = 50 });

and write, we get:

image_thumb2

We get an error.  Xml serialization does not like this.  This is because when it is serializing the Family object, it does not know about the Father and Mother classes.  The Members member is just a list of FamilyMembers.

There are actually 2 fixes for this, each produces different results.

The first solution is to add the [XmlInclude] attribute before the FamilyMember class, once for each sub-class.  This notifies the Xml serialization engine about the sub-classes.

[Serializable]
[XmlInclude(typeof(Father)), 
  XmlInclude(typeof(Mother))]
public class FamilyMember
{
...
	

Now, if we try writing again, we'll get:

<Family xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        Name="Smith">
  <Members>
    <FamilyMember xsi:type="Father">
      <Name>John</Name>
      <Age>50</Age>
      <GolfHandicap>12</GolfHandicap>
    </FamilyMember>
    <FamilyMember xsi:type="Mother">
      <Name>Mary</Name>
      <Age>29</Age>
    </FamilyMember>
  </Members>
</Family>

and this will correctly read as well.

The second solution is to instead use the [XmlArrayElement] attribute on the Members member of the Family class.

[XmlArrayItem(typeof(Father)),
  XmlArrayItem(typeof(Mother))]
public List<FamilyMember> Members
{
...

If we write using this, we'll get:

<Family xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
        Name="Smith">
  <Members>
    <Father>
      <Name>John</Name>
      <Age>50</Age>
      <GolfHandicap>12</GolfHandicap>
    </Father>
    <Mother>
      <Name>Mary</Name>
      <Age>29</Age>
    </Mother>
  </Members>
</Family>

If you have existing Xml files to be read, the second solution will more likely match your data.

So there you have it.

There's many more details and attributes that you can get into for working with Xml files, but this will give you a start for many common cases you may require.

Xml serialization in C# is very easy.  If you've read the code above, you'll notice that there isn't a single bit of code dealing with actual parsing of Xml.  All that is handled automatically by magic the C# engine.  Reading an existing Xml file is as simple as creating a set of classes for the different elements in the data and using attributes to tweak the reading a bit.

I've included the project above.  It's based on C# 3.5 using Visual Studio 2008, but can easily be modified for C# 2.0.

Comments are closed