(Updated 9 Oct 2008: replaced my custom MemoryStream.CopyUpToSeekPointer() extension method with MemoryStream.ToArray(), a built in method on MemoryStream that I overlooked and should have been using)
It's surprising that XmlDocument isn't marked [Serializable], because it's very natural to serialize one into a stream. I wanted to put an object into ASP.NET ViewState the other day, and quickly ran into this roadblock, because part of the object included an XmlDocument, which is not serializable. A quick search revealed that most people deal with this problem by storing a string instead. Indeed, that was where I started, but I quickly realized that there are multiple places in my code where I want to do this sort of thing, and I don't want to have to mess with it in each data structure that contains an XmlDocument.
So I put together a simple class that holds an XmlDocument and implements ISerializable and called it SerializableXmlDocument. I'm sharing the source code here in the hopes that
a) somebody will find it useful, and
b) somebody smarter than I am will point out how I screwed it up and help me make it better.
SerializableXmlDocument includes implicit conversion operators to make it easy to convert to/from an XmlDocument. It holds the actual document in a property called Value. This "isomorph" pattern is one that I picked up from Craig.
Here is SerializableXmlDocument.cs:
using System;
using System.Runtime.Serialization;
using System.Xml;
using System.IO;
namespace Pluralsight.Samples
{
[Serializable]
public class SerializableXmlDocument : ISerializable
{
public SerializableXmlDocument() { }
public SerializableXmlDocument(XmlDocument value)
{
this.Value = value;
}
public XmlDocument Value { get; set; }
#region ISerializable implementation
public SerializableXmlDocument(SerializationInfo info,
StreamingContext context)
{
byte[] serializedData = (byte[])info.GetValue("doc",
typeof(byte[]));
if (null != serializedData)
this.Value = Deserialize(serializedData);
}
public void GetObjectData(SerializationInfo info,
StreamingContext context)
{
byte[] serializedData = null;
if (null != Value)
serializedData = Serialize(Value);
info.AddValue("doc", serializedData);
}
#endregion
#region implicit conversion to/from XmlDocument
public static implicit operator SerializableXmlDocument(
XmlDocument doc)
{
return new SerializableXmlDocument(doc);
}
public static implicit operator XmlDocument(
SerializableXmlDocument sdoc)
{
return sdoc.Value;
}
#endregion
#region Xml serialization helper methods
private static byte[] Serialize(XmlDocument doc)
{
MemoryStream stream = new MemoryStream();
doc.Save(stream);
return stream.ToArray();
}
private static XmlDocument Deserialize(byte[] serializedData)
{
XmlDocument doc = new XmlDocument();
doc.Load(new MemoryStream(serializedData, false));
return doc;
}
#endregion
}
}
...and here's a sample object that uses SerializableXmlDocument:
using System;
namespace Pluralsight.Samples
{
[Serializable]
public class Item
{
public string Name { get; set; }
public SerializableXmlDocument Data { get; set; }
public void Print()
{
Console.WriteLine("Name: {0}", Name);
Console.WriteLine(Data.Value.OuterXml);
}
}
}
...and here's a sample program that creates an instance of Item, serializes it, then deserializes it, printing diagnostics along the way to show that it's working properly.
using System;
using System.Xml;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
using Pluralsight.Samples;
class DemoProgram
{
static void Main(string[] args)
{
XmlDocument doc = new XmlDocument();
doc.LoadXml("<root><child>text</child></root>");
Item item = new Item
{
Name = "Testing 123",
Data = doc,
};
// print object before serialization
item.Print();
BinaryFormatter formatter = new BinaryFormatter();
MemoryStream stream = new MemoryStream();
formatter.Serialize(stream, item);
byte[] serializedItem = stream.CopyUpToSeekPointer();
Console.WriteLine("Serialized data (base64): {0}",
Convert.ToBase64String(serializedItem));
item = (Item)formatter.Deserialize(
new MemoryStream(serializedItem, false));
// print object after deserialization
item.Print();
}
}
Here's the output of the previous sample program:
Flame away!
Posted
Aug 18 2008, 08:58 PM
by
keith-brown