Click here to Skip to main content
Click here to Skip to main content

Dynamic, Object to Object Converter

, 2 Apr 2008
Rate this:
Please Sign up or sign in to vote.
How to use attributes to create a dynamic converter so that you do not need to create a new converter for every conversion.

Introduction

In this article I hope to show you how to use attributes to create a dynamic converter so that you do not need to create a new converter for every conversion. I developed this to facilitate me in converting a DTO object to a Domain (Entity) object.

What this article assumes you know or have:

  • You can read C# code.
  • You have some version of .NET Framework installed, my program demo uses Framework 2.0.
  • You have some idea of attributes and their use.

What a Converter is and Where it is Useful

A converter in general terms is a class used to facilitate he conversion of one object to another. When using a Test Driven Model View Presenter Pattern I needed to convert DTO objects to Domain (Entity) objects and vice versa. Before I had to create a converter for every conversion, suppose there were 500 objects to be converted, I would have had to create 500 converters that would be messy and would mean lot of maintenance in the future.

Project Guidelines

The project we will create shall include defining custom Attributes, Converter class, and a Test class.

If you are using VS.NET create a new project called ‘Converter’ and add a folder ‘Attributes’ to it. Within the folder create a class ValueFromPropertyAttribute.cs. Copy the code from below.

ValueFromPropertyAttribute.cs

using System;

namespace Amyn.Converter.Attributes
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple=false)]
    public class ValueFromPropertyAttribute : Attribute
    {
        private string _propertyName = string.Empty;
        private System.Type _fromType = null;
        private System.Type _toType = null;
        
        public ValueFromPropertyAttribute(string propertyName)
        {
            this._propertyName = propertyName;
        }

        public ValueFromPropertyAttribute(string propertyName, System.Type
                  fromType)
        {
            this._propertyName = propertyName;
            this._fromType = fromType;
        }

        public ValueFromPropertyAttribute(string propertyName, System.Type fromType,
            System.Type toType)
        {
            this._propertyName = propertyName;
            this._fromType = fromType;
            this._toType = toType;
        }

        public string ValueFromProperty { get { return this._propertyName; } }

        public System.Type FromType { get { return this._fromType; } }
        public System.Type ToType { get { return this._toType; } }
}    }

That’s all there in the creation of the Attribute class. As you can see it's just a class that inherits from System.Attribute, and has three properties.

Now let’s create the Converter class.

Converter.cs

using System;
using System.Collections;
using System.Reflection;
using Amyn.Converter.Attributes;

namespace Amyn.Converter
{
    public static class Converter
    {
        public static object ConvertAll(object[] fromObjects, System.Type
          toType)
        {
            if (fromObjects != null)
            {
                ArrayList list = new ArrayList(fromObjects.Length);

                foreach (object obj in fromObjects)
                {
                    list.Add(Convert(obj, toType));
                }

                return list.ToArray(toType);
               
            }
            return null;
        }

        public static object Convert(object fromObject, System.Type toType)
        {
            if (fromObject == null) return null;
            object returnObject = Activator.CreateInstance(toType);

            PropertyInfo [] infos = returnObject.GetType().GetProperties();
            foreach (PropertyInfo property in infos)
            {
                 ValueFromPropertyAttribute [] attributes = 
                     (ValueFromPropertyAttribute
                  [])property.GetCustomAttributes(typeof    
                       (ValueFromPropertyAttribute), false); false);

                if (attributes.Length > 0)
                {
                    PropertyInfo fromProperty = 
                     fromObject.GetType().
                      GetProperty(attributes[0].ValueFromProperty);

                    if (attributes[0].FromType == null)
                    {
                        
                        property.SetValue(returnObject, 
                 fromProperty.GetValue
                   (fromObject, null), null);
                    }
                    else
                    {
                        if (fromProperty.PropertyType.IsArray)
                        {
                            property.SetValue(returnObject, 
                    ConvertAll((object [])
                    fromProperty.GetValue(fromObject, null),
                    attributes[0].ToType), null);
                        }
                        else
                        {
                            property.SetValue(returnObject, 
                    Convert(fromProperty.GetValue(fromObject, 
                    null), property.PropertyType), null);
                        }
                    }
                }
            }

            return returnObject;
        }
   
    }
}

The Converter class has two public methods:

ConvertAll which takes two parameters, an array of objects to convert from ‘fromObjects’, and Type of the object to convert to ‘toType’. It returns an ArrayList of objects of type toType after conversion. Inside the body it checks for null of fromObjects. If not null it iterates through the fromObjects array and calls the Convert method on each iteration.

The Convert method does the bulk of the job. It takes an object to convert from ‘fromObject’ and Type of object to convert to ‘toType’. It returns an object of Type toType after conversion. The System.Activator.CreateInstance method creates an instance of the specified Type (toType) using that Type’s default constructor. System.Reflection.PropertyInfo is used to discover the attributes of the property and provide access to its metadata. All the work is done by the framework with the call to GetCustomAttributes on the Type object. GetCustomAttributes takes two parameters, the first is a Type object for the attribute we wish to get, the second is a Boolean telling the framework whether it should look at the types that this class derives from. GetCustomAttributes returns an object array containing each of the attributes found that match the Type passed in. In this case we request all attributes of a specific type, so we can safely cast that to an array of our attribute ‘ValueFromPropertyAttribute’. Once it has got an array of ‘ValueFromPropertyAttribute’ it determines how many are in the array, if there are none in the array we know that the class does not have the ‘ValueFromPropertyAttribute’ applied to it, else we take the first one in the array, using PropertyInfo.SetValue and PropertyInfo.GetValue we set the properties of the return object.

Let's now create a class to test our Dynamic Custom Converter. Create a new project in the solution and call it TestHarness.

TestHarness

using System;
using Amyn.Converter;
using Amyn.Converter.Attributes;

namespace TestHarness
{
    class Program
    {
        static void Main(string[] args)
        {
            FromTestClass[] testArray = new FromTestClass[2]
            {
                 new FromTestClass() 
                 { 
                     FromPropertyBool=true, 
                     FromPropertyString="TestString 1",
                     FromPropertyClass=new FromTestClass[1]
                     {
                         new FromTestClass(){ FromPropertyBool=false, 
                   FromPropertyString="String inner",
                   FromPropertyClass=null }

                     }
                 }, 
                 new FromTestClass() 
                 { 
                     FromPropertyBool=true, 
                     FromPropertyString="Test String 2" ,
                     FromPropertyClass=new FromTestClass[2] 
                     {
                         new FromTestClass()
                         { 
                             FromPropertyBool=false, 
                             FromPropertyString="String inner", 
                             FromPropertyClass=new FromTestClass[1]
                             {
                                 new FromTestClass(){
                        FromPropertyBool=false,
                        FromPropertyString="String inner 
                        inner", FromPropertyClass=null }
                             }
                         },
                         new FromTestClass(){ FromPropertyBool=false,  
                  FromPropertyString="String inner",
                  FromPropertyClass=null }
                     }
                 }
            };
            

ToTestClass [] returnAll = (ToTestClass   [])Converter.ConvertAll(testArray,
    typeof(ToTestClass));

              foreach (ToTestClass returnConverted in returnAll)
                  {
Console.WriteLine(returnConverted.ToPropertyString);             Console.WriteLine(
    returnConverted.ToPropertyBool.ToString());
            }
            Console.ReadLine();
        }
    }

    class FromTestClass
    {
        private string fromPropertyString;
        private bool fromPropertyBool;
        private FromTestClass [] _fromClass;

        public FromTestClass() { }

        public string FromPropertyString { get { return this.fromPropertyString; } set {
            fromPropertyString = value; } }

        public bool FromPropertyBool { get { return this.fromPropertyBool; } set {
            fromPropertyBool = value; } }

        public FromTestClass [] FromPropertyClass { get { return this._fromClass; } set {
            this._fromClass = value; } }
    }


    class ToTestClass
    {
        private string toPropertyString;
        private bool toPropertyBool;
        private ToTestClass [] _testClass;

        public ToTestClass() { }

        [ValueFromProperty("FromPropertyString")]
        public string ToPropertyString { get { return this.toPropertyString; } set {
            toPropertyString = value; } }

        [ValueFromProperty("FromPropertyBool")]
        public bool ToPropertyBool { get { return this.toPropertyBool; } set {
            toPropertyBool = value; } }

        [ValueFromProperty("FromPropertyClass", typeof(FromTestClass),
            typeof(ToTestClass))]
        public ToTestClass [] ToPropertyClass{ get { return this._testClass; } set {
            this._testClass = value; } }

    }
}

Summary

In the present development scenario, Pattern designing is an essential way of developing projects. In Model View Pattern conversion of objects from one type to another is a day to day need. Decorate the properties of the class to convert to with the custom attribute and the conversion is done.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Amyn Batliwala
Web Developer
United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
GeneralQuick Question to Amyn Pinmemberrgrona22-Oct-08 12:41 
GeneralRe: Quick Question to Amyn Pinmemberbroxxy6-Jul-09 22:20 
GeneralExtending this application Pinmemberguyaton18-Apr-08 8:38 
GeneralRe: Extending this application PinmemberAmyn Batliwala21-Apr-08 9:45 
GeneralRe: Extending this application Pinmemberguyaton22-Apr-08 2:49 
QuestionDemo? PinmemberDoncp2-Apr-08 11:46 
AnswerRe: Demo? PinmemberAmyn Batliwala3-Apr-08 1:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 2 Apr 2008
Article Copyright 2008 by Amyn Batliwala
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid