Customize XML Serialization using IXmlSerializable

XML Serialization in .NET provides an incredibly useful (and easy) way to turn objects into XML and back again. However, in some situations, you may need more control over how your object is serialized. Recently, I found myself needing to serialize an object that contained a number of property name/values - stored using a NameValueCollection. However, you can't serialize this using the XML serializer (see this KB article). I could have switched to using an array - at least for the serialization part - but this would have serialized the whole lot as a series of elements - and I wanted to do this using attributes.

For example, suppose I had the following XML:

<ObjectProperty Name="Test" ReadOnly="false">
    <ProviderInfo Name="SqlServer" TypeCode="VarChar" TypeSize="100" />
    <ProviderInfo Name="Access" TypeCode="VarChar" TypeSize="100" />
</ObjectProperty>

I wanted this to be turned into my ObjectProperty object (with properties Name,ReadOnly and an array of ProviderInfo objects). In addition, each ProviderInfo object has a Name property, and a NameValueCollection that I wanted populating with TypeCode mapped to VarChar and so on. My ProviderInfo object looked like this:

[Serializable]
public class ProviderInfo /* : IXmlSerializable */ {
  NameValueCollection _attributes;
  string _name;

  public ProviderInfo()
  {
  }

  public ProviderInfo(string name, NameValueCollection attributes)
  {
    this._name = name;
    this._attributes = attributes;
  }

  [XmlAttribute]
  public string Name
  {
    get { return _name; }
    set { _name = value; }
  }

  [XmlIgnore]
  public NameValueCollection Attributes
  {
    get { return _attributes; }
    set { _attributes = value; }
  }
}

In the end, this turned out to be simpler than I'd first feared, and the trick was to implement the IXmlSerializable interface (undocumented in .NET 1.1 - hopefully less so in .NET 2.0). The interface requires us to implement three methods; GetSchema(), ReadXml(XmlReader r) and WriteXml(XmlWriter r). When you implement the interface, these methods are called instead of .NET trying to serialize the object itself.

As GetSchema is only used as present by the DataSet, you can escape by returning null for this.

public XmlSchema GetSchema()
{
  return null;
}

Next up, we had to write some code to generate the attributes we needed - for this, you can simply use the WriteAttributeString method of the XmlWriter that we're passed to churn out attributes for the one fixed property of our object (Name), and then the elements in the NameValueCollection.

public void WriteXml(XmlWriter w)
{
  w.WriteAttributeString("Name", _name);
  foreach (string key in _attributes.Keys)
  {
    string value = _attributes[key];
    w.WriteAttributeString(key,value);
  }
}

For ReadXml, we simply had to do the reverse - fetching the "Name" attribute, and dumping the rest into our NameValueCollection.

public void ReadXml(XmlReader r)
{
    _attributes = new NameValueCollection();
    _name = r.GetAttribute("Name");
    while (r.MoveToNextAttribute())
        if (r.Name!="Name")
            _attributes.Add(r.Name, r.Value);
    r.Read();
}

And that's it! If you want a fully working example project, just download the attached source code.

You might also like...

Comments

James Crowley James first started this website when learning Visual Basic back in 1999 whilst studying his GCSEs. The site grew steadily over the years while being run as a hobby - to a regular monthly audience ...

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Linux is only free if your time has no value” - Jamie Zawinski