pavanarya – Pavan Kumar Aryasomayajulu

Contact Email : pavan.aryasomayajulu@gmail.com

Archive for July 2012

Creating a Generic List collection from Xml Using Reflection

with one comment

Hi in this post i want to create a small application which accepts a xml string of specific format and it returns a List. Even though we have serialization and deserialization to accomplish this task, i am writing this example so that we can learn some beautiful things about reflection and other concepts. I got inspired from Xml serializationa and deserialization and thought of writing this.

Format of the xml my Code accepts

<?xml version='1.0'?>
   <List type='DynamicCollections.Info'>
       <Info><FirstName>Kiran</FirstName><lastname>Mantri</lastname><age>23</age></Info>
       <Info><FirstName>Pavan</FirstName><lastname>Kumar</lastname><age>23</age></Info>
    </List>

There is a root tag called List and it has an attribute called type. This attribute value is nothing but the source of our List collection.

List tag contains Info node. This “Info” is the name of the class that our List collection accepts.
FirstName , LastName and age are the attributes of our Info class.

First step : Creating a List and assigning the Source for List dynamically

            XDocument doc = XDocument.Parse(xmlfile.ToString());
            XPathNavigator nav = doc.CreateNavigator();
            string ClassType = nav.SelectSingleNode("List").GetAttribute("type", "");
            //In the above line trying to get class name from List node's attribute
            
            Type typeofList = typeof(System.Collections.Generic.List<>);//Returns type of List class
            Type[] arrType = typeofList.GetGenericArguments();//It returns all the arguments that List accepts. Here it will be an array with one element (T). If we take Dictionary it return array of size 2 with key and value respectively.
            
            Type typofInfoClass = Type.GetType(ClassType);

            //Getting the type of the class that we got from Xml(Info class).
            // Note : Here class name should be fully qualified name in the xml.

            PropertyInfo[] propOfInfo = typofInfoClass.GetProperties();// Here we are getting list of properties associated with class.This is the beauty of reflection 🙂

            Type changedTypeOfListClass = typeofList.MakeGenericType(new Type[] { typofInfoClass });

// Here we are trying to assign a new type to the arguments(source) of List.MakeGenericType accepts an array of Type and returns a Type.These array values are going to be our new List's arguments. So updating our list argumets from T to Info

            dynamic objOfList = Activator.CreateInstance(changedTypeOfListClass);//At last creating an object of the List and this accepts an object of Info class 

Looping through xml and creating object of Info class

Here i am using Linq to loop through xml and getting required values.

var infoobjFromXml = from inf in doc.Descendants("Info")
                                 select new
                                 {
                                     FirstName = inf.Element("FirstName").Value,
                                     lastname = inf.Element("lastname").Value,
                                     age = inf.Element("age").Value
                                 };

infoobjFromXml this an Ienumerable object and we can enumerate through this object and retrieve values like Firstname,lastname,age.

 
foreach (var eachinfobjFromXml in infoobjFromXml)// Looping on the data objects we got from Linq query
            {

// In the next 3 lines we are trying to get properties of each object obtained from linq ie here we are trying to get property name of present in the xml.In 3rd line we are creatin an object of Info class by passing Info class's type(typeofInfoClass)

                Type infoobjXmlType = eachinfobjFromXml.GetType();
                PropertyInfo[] infoObjFromXmlProperties = infoobjXmlType.GetProperties();
                dynamic InfoClassObject = Activator.CreateInstance(typofInfoClass);

//Now we are trying to loop on the properties of the object that we got from linq query. Inside this foreach loop we are going to check whether these properties obtained from linq object exists in our class Info. if they are present then we are going to get these property values and add it to Info object.

                foreach (var eachPropertyOfInfoObjFromXml in infoObjFromXmlProperties)
                {
//Here we are checking if propOfInfo(property array of Info class) contains eachPropertyOfInfoObjFromXml(property of the object that we got from linq). If they are present then we are going to get the property value from an individual object we got from linq query.
//So we are indirectly looping on the Info node of xml and getting the values of Firstname,lastname and age and also at the same time checking whether these values(firstname,lastname,age) exists in Info class or not.
IsPropertyNamesExists is the extension method that checks whether properties of Info class and equiavlent xml nodes were matching or not.

                    bool doesPropertyExists = propOfInfo.IsPropertyNamesExists(propOfInfo, eachPropertyOfInfoObjFromXml);
                    if (doesPropertyExists)
                    {
                        object strvale = infoobjXmlType.GetProperty(eachPropertyOfInfoObjFromXml.Name).GetValue(eachinfobjFromXml, null);


                        PropertyInfo p = typofInfoClass.GetProperty(eachPropertyOfInfoObjFromXml.Name);
                        p.SetValue(InfoClassObject, strvale, null);
                    }
                }
// So we are done with assigning properties that we got from xml to Info class object. Now we are adding them to our List.
                objOfList.Add(InfoClassObject); // Here the beauty of dynamic keyword is if we dont make InfoClassObject as dynamic then it
                // would be of type object and we are suppoed to manually typecast to DynamicCollection.Info class
                //If we declare InfoClassObject as dynamic it will take care of typecasting
            }

    public static class IsPropertyNamesExist
    {
        public static bool IsPropertyNamesExists(this PropertyInfo[] inf, PropertyInfo[] arryProperties, PropertyInfo propertyToBeCompared)
        {
            bool returnValue = false;
            foreach(var property in arryProperties)
            {
                if (property.Name.ToLower() == propertyToBeCompared.Name.ToLower())
                {
                    returnValue = true;
                    
                }
                
            }
            return returnValue;
        }
    }

This method compares each property of each object that we got from linq query with PropertyInfo[] array of Info class.

Here is the complete code .

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
using System.Xml.XPath;
using System.Xml.Linq;
using System.Reflection;

namespace DynamicCollections
{
    class Program
    {
        static void Main(string[] args)
        {
            StringBuilder xmlfile=new StringBuilder("<?xml version='1.0'?>");
            xmlfile.Append(@"<List type='DynamicCollections.Info'>");
            xmlfile.Append("<Info>");
               xmlfile.Append( "<FirstName>Kiran</FirstName>");
                   xmlfile.Append( "<lastname>Mantri</lastname>");
                       xmlfile.Append( "<age>23</age>");
           xmlfile.Append( "</Info>");
           xmlfile.Append("<Info>");
           xmlfile.Append("<FirstName>Pavan</FirstName>");
           xmlfile.Append("<lastname>Kumar</lastname>");
           xmlfile.Append("<age>23</age>");
           xmlfile.Append("</Info>");
           xmlfile.Append("</List>");

           dynamic ob = XmlToList(xmlfile.ToString());

           }

        public static object XmlToList(string xmlfile)
        {

            XDocument doc = XDocument.Parse(xmlfile.ToString());
            XPathNavigator nav = doc.CreateNavigator();
            string ClassType = nav.SelectSingleNode("List").GetAttribute("type", "");

            var infoobjFromXml = from inf in doc.Descendants("Info")
                                 select new
                                 {
                                     FirstName = inf.Element("FirstName").Value,
                                     lastname = inf.Element("lastname").Value,
                                     age = inf.Element("age").Value
                                 };


            Type typeofList = typeof(System.Collections.Generic.List<>);
            Type[] arrType = typeofList.GetGenericArguments();


            Type typofInfoClass = Type.GetType(ClassType);
            PropertyInfo[] propOfInfo = typofInfoClass.GetProperties();


            Type changedTypeOfListClass = typeofList.MakeGenericType(new Type[] { typofInfoClass });
            dynamic objOfList = Activator.CreateInstance(changedTypeOfListClass);

            foreach (var eachinfobjFromXml in infoobjFromXml)
            {
                Type infoobjXmlType = eachinfobjFromXml.GetType();
                PropertyInfo[] infoObjFromXmlProperties = infoobjXmlType.GetProperties();
                dynamic InfoClassObject = Activator.CreateInstance(typofInfoClass);
                foreach (var eachPropertyOfInfoObjFromXml in infoObjFromXmlProperties)
                {

                    bool doesPropertyExists = propOfInfo.IsPropertyNamesExists(propOfInfo, eachPropertyOfInfoObjFromXml);
                    if (doesPropertyExists)
                    {
                        object strvale = infoobjXmlType.GetProperty(eachPropertyOfInfoObjFromXml.Name).GetValue(eachinfobjFromXml, null);


                        PropertyInfo p = typofInfoClass.GetProperty(eachPropertyOfInfoObjFromXml.Name);
                        p.SetValue(InfoClassObject, strvale, null);
                    }
                }

                objOfList.Add(InfoClassObject); // Here the beauty of dynamic keyword is if we dont make InfoClassObject as dynamic then it
                // would be of type object and we are suppoed to manually typecast to DynamicCollection.Info class
                //If we declare InfoClassObject as dynamic it will take care of typecasting
            }
            return objOfList;
        }
    }

    public class Info
    {
        public string FirstName { set; get; }
        public string lastname { set; get; }
        public string age { set; get; }
    }

    public static class IsPropertyNamesExist
    {
        public static bool IsPropertyNamesExists(this PropertyInfo[] inf, PropertyInfo[] arryProperties, PropertyInfo propertyToBeCompared)
        {
            bool returnValue = false;
            foreach(var property in arryProperties)
            {
                if (property.Name.ToLower() == propertyToBeCompared.Name.ToLower())
                {
                    returnValue = true;
                    
                }
                
            }
            return returnValue;
        }
    }
}

Note: This functionality can be achieved using Xml serialization and deserialization. But my intention is to explore the beauty of reflection and how we can specify the source of a generic list collection dynamically.

Thanks,
pavan

Written by pavanarya

July 18, 2012 at 10:24 pm

Posted in Asp.net, c#, Reflection