Dynamic / Anonymous types in C# was a great improvement to the framework, made in version 3.0. I use it a lot when doing AJAX callbacks from JavaScript to ASP.NET MVC Controllers, not to forget the extensive use of anonymous types already used in ASP.NET MVC.
Then yesterday, one case where I absolutely needed to use anonymous types was in my application’s logging service. I want to be able to save behaviors/actions as well as errors. I have two separate tables, but both behaviors and errors can provide a set of details. To do the actual logging, I call an implementation of this interface:
/// <summary>
/// Defines methods for logging errors and behavior.
/// </summary>
[ServiceContract]
public interface ILogService
{
/// <summary>
/// Logs the behavior with data.
/// </summary>
/// <param name="applicationInstanceId">The application instance id.</param>
/// <param name="action">The action.</param>
/// <param name="logDate">The log date.</param>
/// <param name="userAgent">The user agent.</param>
/// <param name="behaviorData">The behavior data.</param>
/// <param name="source">The source.</param>
[OperationContract]
void LogBehaviorWithData(int applicationInstanceId, string action, DateTime logDate, string userAgent, string behaviorData, string source);
/// <summary>
/// Logs the behavior.
/// </summary>
/// <param name="applicationInstanceId">The application instance id.</param>
/// <param name="action">The action.</param>
/// <param name="logDate">The log date.</param>
/// <param name="userAgent">The user agent.</param>
/// <param name="source">The source.</param>
[OperationContract]
void LogBehavior(int applicationInstanceId, string action, DateTime logDate, string userAgent, string source);
}
In the LogBehaviorWithData method, I wanted to specify behaviorData as XML, since the column in the database is an XML column. I do this, so that I’m able to query the table and using XPath to filter on behavior data. That requires me to send XML to the method, and I don’t want to fool around manually with an XmlDocument or something similar.
I was looking around the internet for a way to serialize an anonymous type to XML, and came across a few questions on StackOverflow. The first one accepted there’s no way to do that – even though there was a fine answer below, and the second didn’t provide a solution. I took the code provided by Matthew Whited in his excellent answer (that I don’t believe is not the correct answer of the question). It worked out of the box, except for Arrays, so that needed some extensions.
How to use it
It’s simply an Extension Method of the object type, called ToXml(). And it is used like this:
object d = new
{
Username = "martin",
Roles = new[]
{
"Developer",
"Administrator"
}
};
XElement xml = d.ToXml();
string xmlString = xml.ToString();
The output is a beautifully formatted XML string:
<object>
<Username>martin</Username>
<Roles>
<RolesChild>Developer</RolesChild>
<RolesChild>Administrator</RolesChild>
</Roles>
</object>
To make it more database friendly, you can omit the formatting by specifying SaveOptions in the ToString method call:
string xmlString = xml.ToString(SaveOptions.DisableFormatting);
The actual code
The actual code is quite simple, yet there’s some fiddling around with different types and such. I guess the name of child elements could also need some improvement, preferably changing the collection name to a singular representation and use that as the element name of its children.
using System;
using System.Linq;
using System.Reflection;
using System.Xml;
using System.Xml.Linq;
/// <summary>
/// Extension methods for the dynamic object.
/// </summary>
public static class DynamicHelper
{
/// <summary>
/// Defines the simple types that is directly writeable to XML.
/// </summary>
private static readonly Type[] _writeTypes = new[] { typeof(string), typeof(DateTime), typeof(Enum), typeof(decimal), typeof(Guid) };
/// <summary>
/// Determines whether [is simple type] [the specified type].
/// </summary>
/// <param name="type">The type to check.</param>
/// <returns>
/// <c>true</c> if [is simple type] [the specified type]; otherwise, <c>false</c>.
/// </returns>
public static bool IsSimpleType(this Type type)
{
return type.IsPrimitive || _writeTypes.Contains(type);
}
/// <summary>
/// Converts an anonymous type to an XElement.
/// </summary>
/// <param name="input">The input.</param>
/// <returns>Returns the object as it's XML representation in an XElement.</returns>
public static XElement ToXml(this object input)
{
return input.ToXml(null);
}
/// <summary>
/// Converts an anonymous type to an XElement.
/// </summary>
/// <param name="input">The input.</param>
/// <param name="element">The element name.</param>
/// <returns>Returns the object as it's XML representation in an XElement.</returns>
public static XElement ToXml(this object input, string element)
{
if (input == null)
{
return null;
}
if (String.IsNullOrEmpty(element))
{
element = "object";
}
element = XmlConvert.EncodeName(element);
var ret = new XElement(element);
if (input != null)
{
var type = input.GetType();
var props = type.GetProperties();
var elements = from prop in props
let name = XmlConvert.EncodeName(prop.Name)
let val = prop.PropertyType.IsArray ? "array" : prop.GetValue(input, null)
let value = prop.PropertyType.IsArray ? GetArrayElement(prop, (Array)prop.GetValue(input, null)) : (prop.PropertyType.IsSimpleType() ? new XElement(name, val) : val.ToXml(name))
where value != null
select value;
ret.Add(elements);
}
return ret;
}
/// <summary>
/// Gets the array element.
/// </summary>
/// <param name="info">The property info.</param>
/// <param name="input">The input object.</param>
/// <returns>Returns an XElement with the array collection as child elements.</returns>
private static XElement GetArrayElement(PropertyInfo info, Array input)
{
var name = XmlConvert.EncodeName(info.Name);
XElement rootElement = new XElement(name);
var arrayCount = input.GetLength(0);
for (int i = 0; i < arrayCount; i++)
{
var val = input.GetValue(i);
XElement childElement = val.GetType().IsSimpleType() ? new XElement(name + "Child", val) : val.ToXml();
rootElement.Add(childElement);
}
return rootElement;
}
}
That way it is possible to serialize an anonymous type in C# to XML.
Download
Download the class from my SkyDrive.