Patrick Altman

I am the VP of Engineering at Eldarion, Co-Founder of Amino Software, and a Mentor at jumpstart foundry.

I am equally passionate about writing code as I am about building businesses.

You really should

Posted On

July 3, 2006, 4:32 p.m.

Tags

Do you have an iOS or web application that you'd like help with?

Eldarion offers lean coaching, software development, code reviews, and in some cases staff augmentation.

Give me a call at 615-300-2930 or send us an email and let's see how we can help you.

This site is hosted on

You should consider it for your Django/Python web hosting as well!

IXmlSerializable: A Persistable Example

The Extensible Markup Language, better known as XML, offers tremendous benefits to the application developer, among other arenas. It's self describing nature, allows strong use of decoupling of data from systems in ways that enable these systems to become more robust and extensible.

The example presented here utilized XML as a persistable way to store a representation of business objects that are populated and used within an application. By enabling this mechanism of objects being able to describe how they should be serialized into a persistable format like XML, a robustness is inherently created in your application. It forms one of the foundations for a successful Smart Client (being able to save state when backend services are unavailable for communication. It also provides highly extensible and flexible methods for saving state in stand alone applications. Furthermore, by saving this state in a self describing mechanism, other applications and libraries can now hydrate the objects in the context of a different application, much in the same way files of different types can be shared among applications on one's own machine or across a network with other users.

This is a very powerful extension to the typical business application -- being able to decouple the business objects from the application, much in the same vein that documents are decoupled from word processors. Now, let's get on the example and some code snippets.

For my example, I built a quick WinForms app to demonstrate the serialization/deserialization of the objects created at runtime. Passing references in a ListBox of created objects to the serialization methods and displaying the resulting XML in a TextBox for inspection and then providing the ability to reverse this process, exercising the deserialization methods to hydrate new objects and repopulate the ListBox. In this example, I created the following object hierarchy and structure:

Diagram

As you can tell from this class diagram, the parent class is the Pound object making reference to a real world entity that houses stray animals. The Pound contains, very naturally, a collection of Animals using a List<Animal> generics collection. The Animal class is an abstract class that shares some common properties amongst all types of Animals. Cat and Dog are two specific types of Animals.

Furthermore, Dog has a property called HairLength that is defined by an Enumeration of different choices of HairLength. The key in this class diagram is that both the Pound class and the Animal abstract class both implement IXmlSerializable (System.Xml.Serialization.IXmlSerializable). This allows for this entire object chain to be serialized using XmlSerializer (System.Xml.Serialization.XmlSerializer) with lines of code similar to this:

MemoryStream ms = new MemoryStream();
XmlSerializer xs = new XmlSerializer(pound.GetType());
xs.Serialize(ms, pound);

And deserializing in a very similar fashion:

XmlSerializer xs = new XmlSerializer(typeof(Pound));
MemoryStream ms = new MemoryStream(ASCIIEncoding.ASCII.GetBytes(textBox1.Text));
ms.Position = 0;
Pound pound = (Pound)xs.Deserialize(ms);

As you might notice that the real magic occurs in how XmlSerializer calls the IXmlSerializable implemented methods on the individual objects. The power in this is that the objects describe how to persist and hydrate themselves allowing for the special handling of things like collections and Enumerations. For example, here are the ReadXml() and WriteXml() routines for the Dog class that contains an Enumeration that will be parsed into a serializable format.

public override void ReadXml(XmlReader reader)
{
    if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == GetType().ToString())
    {
        Name = reader["Name"];
        HairLength = (HairLength)Enum.Parse(typeof(HairLength), reader["HairLength"]);
        Key = new Guid(reader["Key"]);
    }
}

public override void WriteXml(XmlWriter writer)
{
    writer.WriteStartElement(GetType().ToString());
    writer.WriteAttributeString("Name", Name);
    writer.WriteAttributeString("HairLength", Enum.GetName(typeof(HairLength), HairLength));
    writer.WriteAttributeString("Key", Key.ToString());
    writer.WriteEndElement();
}

Here you can make note that I am serializing the name representing in the Enumeration and then parsing out the name upon deserialization to hydrate the HairLength property with the proper selection. This is only a simple example of the type of customization that you can do here. You just simple undo what was done to serialize.

For example, you may have a property that has sensitive data in it and would not want to serialize in clear text, you could very easily implement an encryption method here to encrypt upon serialization and decrypt upon deserialization. For objects that contain a collection of serializable objects nested in a property like List<Animal> on the Pound class it is important to remember that you are dealing with XML and therefore have EndElement tags to read past. Here is the ReadXml() and WriteXml() code from the Pound class:

public void ReadXml(XmlReader reader)
{
    reader.Read(); // Skip <Pound>
    if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == GetType().ToString())
    {
        Name = reader["Name"];
        Address = reader["Address"];
        City = reader["City"];
        reader.Read(); // Skip ahead to next node
        if (reader.MoveToContent() == XmlNodeType.Element && reader.LocalName == "Animals")
        {
            reader.Read(); // Skip ahead to next node
            while (reader.MoveToContent() == XmlNodeType.Element && \
                      Type.GetType(reader.LocalName).IsSubclassOf(typeof(Animal)))
            {
                AnimalType = (Animal)Type.GetType(reader.LocalName);
                Animal a = AnimalType.Assembly.CreateInstance(reader.LocalName);
                a.ReadXml(reader);
                Animals.Add(a.Key, a);
                reader.Read(); // Skip to next animal (if there is one)
            }
        }
    }
}

public void WriteXml(XmlWriter writer)
{
    writer.WriteStartElement(GetType().ToString());
    writer.WriteAttributeString("Name", Name);
    writer.WriteAttributeString("Address", Address);
    writer.WriteAttributeString("City", City);
    writer.WriteStartElement("Animals");
    foreach (Animal a in Animals.Values)
    {
        a.WriteXml(writer);
    }
    writer.WriteEndElement();
    writer.WriteEndElement();
}

Ok, here you can see in the WriteXml() method of the Pound class, I serialize in a pretty straight forward manner the native properties of the Pound class. I then loop through the List&lt;Animal&gt; property called Animals, calling the abstract method on each Animal object to serialize itself. This method is not implemented in Animal, but rather in each of it's concrete subclasses, Cat and Dog, in this example.

To have valid XML, I then write out EndElements for both the Animals collection, as well as the Pound Element. Also of note here is the fact that I use GetType().ToString() to specify the name of the element. To me, this is much safer than typing "Pound", as later if I change the name of the class, I don't have to remember to change the ReadXml() or WriteXml(), plus it sets me up for a possible refactoring into something more generic and shared by more classes, if possible, later down the road. Now, note the ReadXml() method on the Pound class.

At first glance it is a bit more complicated than the WriteXml(). In reality, it is mostly just type and safety checks insuring that the XML that you are trying to use to hydrate the object is valid for the object. Notice the use of reader.Read() through the method. This is used to move past certain elements that are built into the structure, mostly the EndElement and to move to the next element when looping.

The zip file contains a complete solution with a WinForms project that exercises this code in a full working example so that you can step through and see what is happening. Enjoy!

Update: Rocky Lhotka posts on a very interesting serialization problem where the object being serialized has events/delegates associated with it. Definately worth the read while on the topic.

Feedback and Commentary

blog comments powered by Disqus