Introduction
I recently starting fooling around with the new XML LinQ stuff in .Net, and it is very impressive. One thing that I found missing was the ability to serialize objects directly into XElements. However, Microsoft did provide the means to get me to hit the ground running and it took a couple of minutes for me to get it right.
Readers and Writers
One thing the classical .Net XML always lacked was an "XmlNodeWriter", a writer that basically allows you to create DOM XML nodes using a standard XmlWriter. There are several implementations, but they can only insert nodes into a document (or a node within that document).
Microsoft has now filled that gap with the XNode.CreateReader() and XContainer.CreateWriter() methods, and using these with the Xml.Serialization classes is dirt easy.
Code
I decided to use extension methods to make it really sweet in terms of .Net 3.5 functionality. So without further adue here is the code:
/// <summary>
/// Represents extension methods.
/// </summary>
public static class ExtensionMethods
{
private static Dictionary<Type, XmlSerializer> _knownTypes
= new Dictionary<Type, XmlSerializer>();
private static ReaderWriterLockSlim _rwl = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
/// <summary>
/// Deserializes an <see cref="XNode"/> to a specific type.
/// </summary>
/// <typeparam name="T">The type of object to deserialize to.</typeparam>
/// <param name="node">The <see cref="XNode"/> to deserialize.</param>
/// <returns>A new object of type <see cref="T"/>.</returns>
public static T Deserialize<T>(this XNode node) where T : class
{
XmlSerializer serializer;
Type t = typeof(T);
using(_rwl.Read(true))
{
if (!_knownTypes.TryGetValue(t, out serializer))
{
using(_rwl.Write())
{
if (!_knownTypes.TryGetValue(t, out serializer))
{
serializer = new XmlSerializer(t);
_knownTypes.Add(t, serializer);
}
}
}
}
using (XmlReader reader = node.CreateReader())
{
return serializer.Deserialize(reader) as T;
}
}
/// <summary>
/// Serializes an object into an <see cref="XContainer"/>.
/// </summary>
/// <param name="target">The target <see cref="XContainer"/>.</param>
/// <param name="value">The value to serialize.</param>
public static void Serialize(this XContainer container, object value)
{
if (value == null)
return;
XmlSerializer serializer;
Type t = value.GetType();
using (_rwl.Read(true))
{
if (!_knownTypes.TryGetValue(t, out serializer))
{
using (_rwl.Write())
{
if (!_knownTypes.TryGetValue(t, out serializer))
{
serializer = new XmlSerializer(t);
_knownTypes.Add(t, serializer);
}
}
}
}
using (XmlWriter writer = container.CreateWriter())
{
serializer.Serialize(writer, value);
}
}
}
You will also need a utility class that I wrote that basically takes the chore of using ReaderWriterLockSlims:
public static class ExtensionMethods
{
private class ReaderWriterLockSlimController : IDisposable
{
private bool _closed = false;
private ReaderWriterLockSlim _slim;
private bool _read = false;
private bool _upgrade = false;
public ReaderWriterLockSlimController(ReaderWriterLockSlim slim, bool read, bool upgrade)
{
_slim = slim;
_read = read;
_upgrade = upgrade;
if (_read)
{
if (upgrade)
{
_slim.EnterUpgradeableReadLock();
}
else
{
_slim.EnterReadLock();
}
}
else
{
_slim.EnterWriteLock();
}
}
#region IDisposable Members
~ReaderWriterLockSlimController()
{
Dispose();
}
public void Dispose()
{
if (_closed)
return;
_closed = true;
if (_read)
{
if (_upgrade)
{
_slim.ExitUpgradeableReadLock();
}
else
{
_slim.ExitReadLock();
}
}
else
{
_slim.ExitWriteLock();
}
GC.SuppressFinalize(this);
}
#endregion
}
public static IDisposable Read(this ReaderWriterLockSlim slim, bool upgradeable)
{
return new ReaderWriterLockSlimController(slim, true, upgradeable);
}
public static IDisposable Read(this ReaderWriterLockSlim slim)
{
return new ReaderWriterLockSlimController(slim, true, false);
}
public static IDisposable Write(this ReaderWriterLockSlim slim)
{
return new ReaderWriterLockSlimController(slim, false, false);
}
}
And finally, something to test it:
[XmlRoot("test")]
public class Test
{
[XmlAttribute("testAttribute")]
public string Foo;
}
class Program
{
static void Main(string[] args)
{
XDocument doc = new XDocument();
Test tst = new Test();
tst.Foo = "Hello World!";
doc.Serialize(tst);
using (XmlWriter writer = new XmlTextWriter(System.Console.Out))
{
doc.WriteTo(writer);
}
Console.ReadLine();
}
}
Conclusion
I feel that this is an oversight, but a small one at that. It took me not 15 minutes to get it right, so I hope this will save you 15 minutes some time in the future.
posted @ Wednesday, August 27, 2008 6:34 PM