Friday, January 06, 2006

An XML Serialization Epiphany

I have an object that I want to serialize so that my webapp (SharePoint) can use its properties later. The object (PropertyClass) has a custom collection property that holds objects of BaseClass. Adding the Serialization [Serializable] attribute to each of the class declarations being serialized (ie PropertyClass, CustomCollection, and BaseClass) let me serialize to my heart's content. The problem started when I added a derived class of the BaseClass to the custom collection. Everything would work until the serialization call and then I would get the following error:

The type DerivedClass was not expected. Use the XmlInclude or SoapInclude attribute to specify types that are not known statically.

After a little googling I found that the problem was that the XMLSerializer did not know how to serialize the derived class. After an hour of googling I found that I needed to add an xml attribute to the BaseClass that tells it how to find the derived classes of the BaseClass. Here’s the xml attribute that I applied to the BaseClass:

[System.Xml.Serialization.XmlInclude(typeof(Class.DerivedClass))]
public class BaseClass
{
...

This seemed to allow me to do normal serialization to a file. However, I needed to serialize this class to custom property of a webpart in SharePoint. SharePoint still choked on the serialization call. After a day of googling I finally found a reference to the XML Schema Definition Tool (xsd.exe). By running the below command I was able to see the xml schema that my serialized PropertyClass would create.

xsd MyProject.dll /t: MyProject.PropertyClass

This command creates an xsd file that has the xml for the schema. After looking at the schema for awhile I found that the schema shows the CustomCollection class as an array. Once this epiphany occurred, I remembered from all the googling that I had already done the XmlArrayItem attribute. I added the following to the get/set property for my CustomCollection in the PropertyClass.

// This attribute enables the Collection to be serialized:
[System.Xml.Serialization.XmlArray( "MyCollection" )]
// Explicitly tell the serializer to expect the derived class so it can be properly written to XML from the collection:
[System.Xml.Serialization.XmlArrayItem( typeof(Class.DerivedClass)) ]
public CustomCollection MyCollection
{
get
{
return myCollection;
}
set
{
myCollection = value;
}
}

And voila, everything serialized beautifully. I have since added several other derived classes to the collection and it works wonderfully as long as you add the appropriate XmlArrayItem and XmlInclude attributes.

No comments: