Monday, August 5, 2013

Parsing XML to Collection of Dynamic Objects (LINQToSQL, dynamic, ExpandoObject, MVC WebGrid)

Recently I have experienced with some of the new features (I guess these components are rarely used) of C# 4.0 such the dynamic type, ExpandoObject. My initial requirement was to build a dynamically created object collection out of XML formatted SQL result. Then it has to be used as a data-source for my grid.



Note: The select statement was dynamically generated and the result were vary from one query to another. My intent was to generate the data-source (list of objects) without using a well-structured mapping-class. With the use of ExpandoObject I was able to generate an object in run-time with dynamic properties.




SQL Query which return a XML formatted result

SELECT TOP 5 OT.Id as Id, O.Name As Name, OT.StartTime As StartTime FROM Call OT
LEFT OUTER JOIN Outlet O ON OT.Outlet_SiteUid=O.SiteUid AND OT.OutletUid=O.Uid
WHERE OT.CreationDate BETWEEN '2011-10-01' AND '2012-10-31'
FOR XML PATH('Result'), ROOT('Results')


XML Formatted result

<Results>
  <Result>
    <Id>CARC00000607</Id>
    <Name>CENTRAL (ZHONG HUAN CHA CAN TING) - 313 @SOMERSET</Name>
    <StartTime>2011-11-28T09:22:28</StartTime>
  </Result>
  <Result>
    <Id>CARC00000608</Id>
    <Name>CENTRAL (ZHONG HUAN CHA CAN TING) - 313 @SOMERSET</Name>
    <StartTime>2011-11-28T10:16:12</StartTime>
  </Result>
</Results>


For ease of explanation purpose I have copied the XML result to a XML file called "SQLResult1.xml".

DynamicXmlConverter helper class

public class DynamicXmlConverter
{
        /// <summary>
             /// http://msdn.microsoft.com/en-us/library/system.dynamic.expandoobject.aspx
             /// http://msdn.microsoft.com/en-us/library/vstudio/dd264741.aspx
             /// </summary>
             /// <param name="descendantName"></param>
             /// <param name="filePath"></param>
             /// <returns>List<dynamic></returns>

        public static List<dynamic> GenerateDatasource(string descendantName, string filePath)
        {
            //intialize list of dynamic objects
            var dataList = new List<dynamic>();

            //xml-file loading
            var doc = XDocument.Load(filePath);

            //selecting all the Descendats of Ex: "Result"
            var descendants = doc.Descendants(descendantName);

            //loop through each Descendats
            foreach (var descendant in descendants)
            {
                //creating the ExpandoObject
                dynamic expandoObject = new ExpandoObject();

                //casting it to a dictionary object
                var dictionaryExpandoObject = (IDictionary<string, object>)expandoObject;

                //loop through each elements of descendant
                foreach (var element in descendant.Elements())
                {
                    //assigning of element name as propertyName
                    var propertyName = element.Name.LocalName;
                    //adding the property name and value to the dictionary
                    dictionaryExpandoObject.Add(propertyName, element.Value);
                }
                //finally add each ExpandoObject to list
                dataList.Add(dictionaryExpandoObject);
            }
            return dataList;
       }

}

Controller

public class HomeController : Controller
{       
        public ActionResult LoadGrid()
        {
            var dataList = DynamicXmlConverter.GenerateDatasource("Result",    Server.MapPath("SQLResult1.xml"));
            return View(dataList);
        }

}

View

@{
    ViewBag.Title = "Home Page";
}
@model System.Collections.IEnumerable
@{

    var grid = new WebGrid((IEnumerable<dynamic>)Model, canPage: true, rowsPerPage: 5);
}
@grid.GetHtml()



Output