An XML serialization helper that includes a serializer cache when targeting heirarchies.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// Delegate for events during the serializer cache. | |
/// </summary> | |
public delegate void SerializerCacheDelegate(); | |
/// <summary> | |
/// XML serialization helper | |
/// </summary> | |
public static class Xml | |
{ | |
/// <summary> | |
/// The NewSerializer event first when the cache receives a request | |
/// for a serializer instance not in the cache. | |
/// </summary> | |
public static event SerializerCacheDelegate NewSerializer; | |
/// <summary> | |
/// The NewSerializer event first when the cache receives a request | |
/// for a serializer instance that exists in the cache. | |
/// </summary> | |
public static event SerializerCacheDelegate CacheHit; | |
static readonly ConcurrentDictionary<string, XmlSerializer> _serializerCache = new ConcurrentDictionary<string, XmlSerializer>(); | |
static object _sync = new object(); | |
static XmlSerializer GetSerializer(Type baseType, Type[] types) | |
{ | |
if (baseType == null) | |
{ | |
throw new ArgumentNullException("Serializer baseType cannot be null."); | |
} | |
if (types == null) | |
{ | |
throw new ArgumentNullException("Serializer types cannot be null. If this can turn up null, consider the easier Serialize<T>()."); | |
} | |
// Generate the key. | |
var b = new StringBuilder(baseType.Name); | |
for (int i = 0; i < types.Length; i++) | |
{ | |
b.Append(':'); | |
b.Append(types[i].Name); | |
} | |
var key = b.ToString(); | |
// Check if the cache contains a cached serializer or not. | |
XmlSerializer serializer; | |
if (!_serializerCache.TryGetValue(key, out serializer)) | |
{ | |
// Lock when adding a new value. | |
lock (_sync) | |
{ | |
serializer = new XmlSerializer(baseType, types); | |
_serializerCache.TryAdd(key, serializer); | |
if (NewSerializer != null) | |
{ | |
NewSerializer(); | |
} | |
} | |
} | |
else | |
{ | |
if (CacheHit != null) | |
{ | |
CacheHit(); | |
} | |
} | |
return serializer; | |
} | |
/// <summary> | |
/// Get a list of all the cached serializer keys. | |
/// </summary> | |
/// <returns></returns> | |
public static IEnumerable<string> GetCache() | |
{ | |
return _serializerCache.Keys.ToArray(); | |
} | |
/// <summary> | |
/// Reset the current cache. | |
/// </summary> | |
public static void ResetCache() | |
{ | |
_serializerCache.Clear(); | |
} | |
/// <summary> | |
/// Serialize the given object into UTF-8 encoded XML | |
/// </summary> | |
/// <param name="item"></param> | |
/// <returns></returns> | |
public static string Serialize(object item) | |
{ | |
if (item == null) | |
{ | |
throw new ArgumentNullException("item"); | |
} | |
using (var stream = new MemoryStream()) | |
{ | |
// This constructor is automatically cached, so no need for the | |
// local cache. | |
var serializer = new XmlSerializer(item.GetType()); | |
serializer.Serialize(stream, item); | |
return Encoding.UTF8.GetString(stream.ToArray()); | |
} | |
} | |
/// <summary> | |
/// Serialize the given object into UTF-8 encoded XML | |
/// </summary> | |
/// <param name="item"></param> | |
/// <param name="types"></param> | |
/// <returns></returns> | |
public static string Serialize(object item, Type[] types) | |
{ | |
if (item == null) | |
{ | |
throw new ArgumentNullException("item"); | |
} | |
if (types == null) | |
{ | |
throw new ArgumentNullException("types"); | |
} | |
var baseType = item.GetType().BaseType; | |
if (baseType == null) | |
{ | |
throw new InvalidOperationException("Type does not derive from anything."); | |
} | |
if (!types.Contains(item.GetType())) | |
{ | |
throw new Exception("Type of T is unknown."); | |
} | |
using (var stream = new MemoryStream()) | |
{ | |
// Get a cached serializer. | |
var serializer = GetSerializer(baseType, types); | |
serializer.Serialize(stream, item); | |
return Encoding.UTF8.GetString(stream.ToArray()); | |
} | |
} | |
/// <summary> | |
/// Deserialize the given UTF-8 encoded XML string | |
/// </summary> | |
/// <typeparam name="T"></typeparam> | |
/// <param name="xml"></param> | |
/// <returns></returns> | |
public static T Deserialize<T>(string xml) | |
{ | |
using (var stream = new MemoryStream(xml.ToByteArray())) | |
{ | |
// This constructor is automatically cached, so no need for the | |
// local cache. | |
var serializer = new XmlSerializer(typeof(T)); | |
return (T)serializer.Deserialize(stream); | |
} | |
} | |
/// <summary> | |
/// Deserialize the given UTF-8 encoded XML string | |
/// </summary> | |
/// <typeparam name="T"></typeparam> | |
/// <param name="xml"></param> | |
/// <param name="types"></param> | |
/// <returns></returns> | |
public static T Deserialize<T>(string xml, Type[] types) | |
{ | |
if (types == null) | |
{ | |
throw new ArgumentNullException("types"); | |
} | |
if (!types.Contains(typeof(T))) | |
{ | |
throw new InvalidOperationException("Type of T is unknown."); | |
} | |
var baseType = typeof(T).BaseType; | |
if (baseType == null) | |
{ | |
throw new InvalidOperationException("Type does not derive from anything."); | |
} | |
return (T)Deserialize(xml, baseType, types); | |
} | |
/// <summary> | |
/// Deserialize the given UTF-8 encoded XML string | |
/// </summary> | |
/// <param name="xml"></param> | |
/// <param name="baseType"></param> | |
/// <param name="types"></param> | |
/// <returns></returns> | |
public static object Deserialize(string xml, Type baseType, Type[] types) | |
{ | |
if (baseType == null) | |
{ | |
throw new ArgumentNullException("baseType"); | |
} | |
if (types == null) | |
{ | |
throw new ArgumentNullException("types"); | |
} | |
using (var stream = new MemoryStream(xml.ToByteArray())) | |
{ | |
// Get a cached serializer. | |
var serializer = GetSerializer(baseType, types); | |
return serializer.Deserialize(stream); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment