Library code snippets
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.
Related articles
Related discussion
-
Creating a Windows Service in VB.NET
by Templario55 (107 replies)
-
Nesting tables in a dataset
by Peterb74 (1 replies)
-
Nesting tables in a dataset
by Peterb74 (0 replies)
-
HL7 requirement - Urgent
by Akhil_Kothari (1 replies)
-
write to XML file vb.net
by acnetonline (2 replies)
Related podcasts
-
Episode 10: LINQ
K Scott leads us in a discussion of LINQ, including: What is it How introducing LINQ to .NET changed the framework LINQ Providers LINQ to XML LINQ to SQL - how it's different from EF, tips and tricks, when to use it Links: LINQpad 3rd Party LIN...
Events coming up
-
Nov
18
15 Minutes of Fame
Dresher, United States
This is a yearly tradition. We select 10 of the favorite speakers from monthly meetings, code camps, and hands on labs. Each one does a 15 minute talk on their favorite .NET technology. This is our 10th anniversary so we plan a gala event with special prizes and refreshments.
I've been looking for some example like this for a while now, it's great.
But I have a question about some error I got.
Using the normal serialization (without overriding the IXmlSerializable interface functions) you are able to add attributes to fields, for example:
<XmlElement("DESCRIPTION")> _
Public Desc As String
And that works fine. It checks the attribute and when it exports the class to XML, Desc is written as DESCRIPTION.
After I implement IXmlSerializable , I get a run time error saying I can't have attributes on members. Why is that?
Also, I did not include the [Serializable] attribute for my class, and it still worked for me. What is the purpose of [Serializable] attribute?
Thanks for any input
This thread is for discussions of Customize XML Serialization using IXmlSerializable.