Serialize C# dynamic and Anonymous types to XML

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.

  • http://www.facebook.com/profile.php?id=505003628 Nick Strupat

    Excellent work.

  • Anonymous

    Awesome post.

    What about deserializing back to object?

  • http://www.milkshakecommerce.com/ecommerce-blog Martin H. Normark

    That’s another story :) I think you’ll have to create a class for that, and use the XML serializer/deserializer. But that makes the anonymous type irrelevant.

  • Devon Burriss

    Source code download not working. Would it be possible to get this? Would really be great to check out!

  • http://www.milkshakecommerce.com/ecommerce-blog Martin H. Normark

    Hi. You can get it here: 
    https://gist.github.com/2574972

    Thanks for the heads up…

  • Fero Chvostaľ

    Nice code, but it looks like it cannot serialize List.

  • http://www.facebook.com/jeff.tian Jeff Tian

    The “if (input != null)” is redundant for ToXml(input, element) method. Because there already had been a null test precedent it.

  • http://www.milkshakecommerce.com/ecommerce-blog Martin H. Normark

    Oh yeah. Nicely spotted!

  • Mohsen

    Hi Martin,

    It’s an excellent post. I just want to share an extension to your code to support Expando objects:

    private static XElement CreateRootElement(string element)
    {
    if (String.IsNullOrEmpty(element))
    {
    element = “object”;
    }
    element = XmlConvert.EncodeName(element);
    var ret = new XElement(element);
    return ret;
    }

    public static XElement ToXml(this ExpandoObject input)
    {
    return ToXml(input, “”);
    }

    public static XElement ToXml(this ExpandoObject input, string element)
    {
    var ret = CreateRootElement(element);

    if (input != null)
    {
    var elements = from prop in input
    let name = XmlConvert.EncodeName(prop.Key)
    let val = prop.Value
    let valType = prop.Value.GetType()
    let value = valType.IsArray
    ? GetArrayElement(prop.Key, (Array)val)
    : (valType.IsSimpleType() ? new XElement(name, val) : val.ToXml(name))
    where value != null
    select value;
    ret.Add(elements);
    }
    else
    {
    return null;
    }
    return ret;
    }

  • Himadri

    Both object and ExpandoObject serialization explanation is simply great. I have one small suggestion on Expando implementation as if the root Expando object having some inner Expando object in that case above code might not work well for inner Expando object. If we can alter the LINQ code as below then it’s working fine.

    var elements = from prop in input
    let name = XmlConvert.EncodeName(prop.Key)
    let val = prop.Value
    let valType = prop.Value.GetType()
    let value = valType.IsArray
    ? GetArrayElement(prop.Key, (Array)val)
    : (valType.IsSimpleType() ? new XElement(name, val) :
    ((val is IDynamicMetaObjectProvider) ? ((ExpandoObject)val).ToXml(name) : val.ToXml(name)))
    where value != null

    select value;

  • Anoni :)

    great code! a really nice place to start off…thanks!

  • Pingback: Using XSLT as email template engine in C# | Axel Zarate

  • Axel Zarate

    Very useful. I modified it a bit to be able to serialize any type of collection and made the class non-static for abstraction purposes. If case anybody needs it, you can get it from this article: http://axelzarate.wordpress.com/2014/08/19/using-xslt-as-email-template-engine-in-c/.

    Thanks!

  • http://martinnormark.com/ Martin H. Normark

    Hey Axel – Nice improvements! The lack of Enumerable support in mine really was a bottleneck. I rarely use pure Arrays these days.