<$BlogRSDURL$>
My Blog Links
Site Links
Currently Reading
Blogroll
Archives
Tools
  • This page is powered by Blogger. Isn't yours?
  • Weblog Commenting and Trackback by HaloScan.com
  • 2RSS.com :: RSS directory
  • Listed on BlogShares
  • Blog Directory, Find A Blog, Submit A Blog, Search For The Best Blogs

Tuesday, August 10, 2004

.NET: XML Serialization Base Class 

Working on WinForms applications, I've written several projects that persist information out to a file. I have found that using XML serialization to save and load the file is an easy way of mapping a class to an XML document.

Serialization in the .NET Framework is very powerful and really pretty easy to implement. Although multiple serializers are available, like XML, binary, text, etc, I've used the XML method most often. Therefore, I created a base class called XmlPersistedClass that I use in projects that want to read and write XML files. The class implements Save and Load methods which encapsulate all of the code required to interact with serializers, readers, and writers.

Here's what it looks like:

using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace MyClassLibrary
{
/// <summary>
/// XmlPersistedClass allows derieved classes to provide Save/Load functionality
/// to and from xml files. The methods are generic and can be overridden or
/// overloaded to provide type specific versions.
/// </summary>
[Serializable]
public abstract class XmlPersistedClass
{
/// <summary>
/// Default constructor -- required for serialization.
/// </summary>
public XmlPersistedClass()
{
}

/// <summary>
/// Saves this class to xml at the specified filename.
/// </summary>
/// <param name="pathFilename">Filename by which to save the class.</param>
/// <param name="classType">The type of the class to save.</param>
virtual public void Save(string pathFilename, Type classType)
{
// make sure the string and type are valid.
if (pathFilename == null || classType == null)
throw new ArgumentNullException();
if (pathFilename.Length < 1)
throw new ArgumentException();

// Create an instance of the XmlSerializer class; specify the type of object to serialize.
XmlSerializer serializer = new XmlSerializer(classType);
TextWriter writer
= new StreamWriter(pathFilename);

// Serialize this object, and close the TextWriter.
serializer.Serialize(writer, this);
writer.Close();
}

/// <summary>
/// Loads a class from xml at the specified filename.
/// </summary>
/// <param name="pathFilename">Filename from which to load the class</param>
/// <param name="classType">The expected type of the class to load.</param>
/// <returns>Returns the class with data loaded from the file.</returns>
static public object Load(string pathFilename, Type classType)
{
// make sure the string and type are valid.
if (pathFilename == null || classType == null)
throw new ArgumentNullException();
if (pathFilename.Length < 1)
throw new ArgumentException();

FileStream stream
= null;
try
{
// Create an instance of the XmlSerializer class;
specify the type of object to be deserialized.

XmlSerializer serializer = new XmlSerializer(classType);

// A FileStream is needed to read the XML document.
stream = new FileStream(pathFilename, FileMode.Open);
// Use the Deserialize method to restore the object's state with data from the XML document.
object objLoaded = serializer.Deserialize(stream);
stream.Close();
return objLoaded;
}
catch (FileNotFoundException)
{
// do nothing, but return null...
return null;
}
finally
{
if (stream != null)
{
stream.Close();
}
}
}
}
}


As you can see this is an abstract class, so that to use it you must derive your own class from it. The Save method instantiates an XmlSerializer with the specified class type, then uses a TextWriter to serialize this class into. The Load method also uses an XmlSerializer and then a TextReader to create an instance of the class from what has been saved in the file.

The following is an example of creating a derived class:
/// <summary>
/// Class that derives from XmlPersistedClass and adds properties to be
/// persisted. Then adds type-specific Load and Save methods.
/// </summary>
public class MyTestClass : XmlPersistedClass
{
// private members
private int number = 0;
private ArrayList list = new ArrayList();

// public properties
[XmlAttribute] public int Number
{
get { return number; }
set { number = value; }
}

[XmlArray]
public ArrayList List
{
get { return list; }
set { list = value; }
}
/// <summary>
/// Default constructor - required for serialization.
/// </summary>
public MyTestClass()
{
}

/// <summary>
/// Saves this class to the specified file.
/// </summary>
/// <param name="fileName">Full path to file.</param>
public virtual void Save(string fileName)
{
base.Save(fileName, typeof(MyTestClass));
}

/// <summary>
/// Loads a new instance of this class from the specified file.
/// </summary>
/// <param name="fileName">Full path to file.</param>
public static MyTestClass Load(string fileName)
{
return (MyTestClass)XmlPersistedClass.Load(fileName, typeof(MyTestClass));
}
}

In the code above, you'll notice that the [Serializable] attribute needs to be on the class, each property needs to have an XML serialization attribute as well, and you need a public default constructor for the class. If you don't specify an attribute for your property, then it defaults to [XmlElement]. However, [XmlAttribute], [XmlArray], and [XmlIgnore] are also very useful. Finally, XML serialization only persists property or data members that are public and have getters and setters. If your property doesn't follow these guidelines, then it won't be saved.

Usually, I overload the Save and Load methods to be type-specific rather than requiring types to be passed into those methods. Finally, this is a good example of methods that could really benefit from the Generics functionality coming out in Visual Studio 2005 -- at some point, I'll update the class to take advantage of that.

Comments: Post a Comment