128

Are there any elegant quick way to map object to a dictionary and vice versa?

Example:

IDictionary<string,object> a = new Dictionary<string,object>();
a["Id"]=1;
a["Name"]="Ahmad";
// .....

becomes

SomeClass b = new SomeClass();
b.Id=1;
b.Name="Ahmad";
// ..........
Community
  • 1
  • 1
Saw
  • 6,199
  • 11
  • 53
  • 104
  • fastest way would be by code-generation like protobuf... I used expression trees to avoid reflection as often as possible – Konrad Mar 24 '20 at 20:40
  • All the self-coded answers below do not support deep conversion and will not work in real life applications. You should use some library like Newtonsoft as suggested by earnerplates – Cesar Jun 22 '20 at 16:45

10 Answers10

226

Using some reflection and generics in two extension methods you can achieve that.

Right, others did mostly the same solution, but this uses less reflection which is more performance-wise and way more readable:

public static class ObjectExtensions
{
    public static T ToObject<T>(this IDictionary<string, object> source)
        where T : class, new()
    {
            var someObject = new T();
            var someObjectType = someObject.GetType();

            foreach (var item in source)
            {
                someObjectType
                         .GetProperty(item.Key)
                         .SetValue(someObject, item.Value, null);
            }

            return someObject;
    }

    public static IDictionary<string, object> AsDictionary(this object source, BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
    {
        return source.GetType().GetProperties(bindingAttr).ToDictionary
        (
            propInfo => propInfo.Name,
            propInfo => propInfo.GetValue(source, null)
        );

    }
}

class A
{
    public string Prop1
    {
        get;
        set;
    }

    public int Prop2
    {
        get;
        set;
    }
}

class Program
{
    static void Main(string[] args)
    {
        Dictionary<string, object> dictionary = new Dictionary<string, object>();
        dictionary.Add("Prop1", "hello world!");
        dictionary.Add("Prop2", 3893);
        A someObject = dictionary.ToObject<A>();

        IDictionary<string, object> objectBackToDictionary = someObject.AsDictionary();
    }
}
Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
  • 1
    I like the implementation, I actually started using it, but do you have any feedback on performance? I know that reflection is not the greatest when it comes to performance? do you have any comments? – nolimit Feb 27 '13 at 18:07
  • @nolimit Actually I don't know its actual performance, but I guess it's not that bad (worst than avoiding reflection, of course...). In fact, what's the alternative? Depending on projects' requirements, maybe there're other options, like using dynamic typing which is far faster than reflection... Who knows! :) – Matías Fidemraizer Feb 27 '13 at 19:02
  • well in my case the alternative is as simple as building the object by assignment, but I'm looking for a neat yet inexpensive way to build an object. Having said that, I have found [this](http://stackoverflow.com/questions/25458/how-costly-is-net-reflection) which means to me that it's not too expensive to use that piece of code. – nolimit Feb 27 '13 at 19:40
  • @nolimit Yeah, it seems that there's no problem. It's just about using the right tool for the required problem :D In your case, it should work like a charm. I believe I can tune up something in the code yet! – Matías Fidemraizer Feb 27 '13 at 19:47
  • @nolimit Check that now `GetType()` isn't called on `someObject` for each property anymore in the first method. – Matías Fidemraizer Feb 27 '13 at 19:50
  • Very nice solution, but if I'm not mistaken this would not handle nested properties right? – Francesco D.M. Jul 10 '20 at 09:24
  • 1
    This solution doesn't work for anonymous objects (e.g when you don't have a type to convert to), for that see https://stackoverflow.com/a/7596697/800817 – Ivan Castellanos Jan 27 '21 at 21:21
  • I'd check that GetProperty does not return null, just in case someone tries to convert a dictionary with keys that do not match the object properties. – Davide Visentin Jun 29 '22 at 15:51
50

Convert the Dictionary to JSON string first with Newtonsoft.

var json = JsonConvert.SerializeObject(advancedSettingsDictionary, Newtonsoft.Json.Formatting.Indented);

Then deserialize the JSON string to your object

var myobject = JsonConvert.DeserializeObject<AOCAdvancedSettings>(json);
learnerplates
  • 4,257
  • 5
  • 33
  • 42
15

Seems reflection only help here.. I've done small example of converting object to dictionary and vise versa:

[TestMethod]
public void DictionaryTest()
{
    var item = new SomeCLass { Id = "1", Name = "name1" };
    IDictionary<string, object> dict = ObjectToDictionary<SomeCLass>(item);
    var obj = ObjectFromDictionary<SomeCLass>(dict);
}

private T ObjectFromDictionary<T>(IDictionary<string, object> dict)
    where T : class 
{
    Type type = typeof(T);
    T result = (T)Activator.CreateInstance(type);
    foreach (var item in dict)
    {
        type.GetProperty(item.Key).SetValue(result, item.Value, null);
    }
    return result;
}

private IDictionary<string, object> ObjectToDictionary<T>(T item)
    where T: class
{
    Type myObjectType = item.GetType();
    IDictionary<string, object> dict = new Dictionary<string, object>();
    var indexer = new object[0];
    PropertyInfo[] properties = myObjectType.GetProperties();
    foreach (var info in properties)
    {
        var value = info.GetValue(item, indexer);
        dict.Add(info.Name, value);
    }
    return dict;
}
lorond
  • 3,856
  • 2
  • 37
  • 52
Andrew Orsich
  • 52,935
  • 16
  • 139
  • 134
12

I'd highly recommend the Castle DictionaryAdapter, easily one of that project's best-kept secrets. You only need to define an interface with the properties you want, and in one line of code the adapter will generate an implementation, instantiate it, and synchronize its values with a dictionary you pass in. I use it to strongly-type my AppSettings in a web project:

var appSettings =
  new DictionaryAdapterFactory().GetAdapter<IAppSettings>(ConfigurationManager.AppSettings);

Note that I did not need to create a class that implements IAppSettings - the adapter does that on the fly. Also, although in this case I'm only reading, in theory if I were setting property values on appSettings, the adapter would keep the underlying dictionary in sync with those changes.

brimble2010
  • 17,796
  • 7
  • 28
  • 45
Todd Menier
  • 37,557
  • 17
  • 150
  • 173
  • 2
    I guess the "best-kept secret" died a lonely death. The Castle DictionaryAdapter link above, as well as the Download links for "DictionaryAdapter" on http://www.castleproject.org/ all go to a 404 Page :( – Shiva Nov 19 '13 at 20:10
  • 1
    No! It's still in the Castle.Core. I have fixed the link. – Gaspar Nagy Nov 19 '13 at 20:46
  • Castle was a great library that got overlooked somehow – bikeman868 Aug 25 '19 at 16:06
6

I think you should use reflection. Something like this:

private T ConvertDictionaryTo<T>(IDictionary<string, object> dictionary) where T : new()
{
    Type type = typeof (T);
    T ret = new T();

    foreach (var keyValue in dictionary)
    {
        type.GetProperty(keyValue.Key).SetValue(ret, keyValue.Value, null);
    }

    return ret;
}

It takes your dictionary and loops through it and sets the values. You should make it better but it's a start. You should call it like this:

SomeClass someClass = ConvertDictionaryTo<SomeClass>(a);
TurBas
  • 1,646
  • 13
  • 15
  • For what you want to use an activator if you can use the "new" generic constraint? :) – Matías Fidemraizer Feb 09 '11 at 11:56
  • @Matías Fidemraizer You are absolutely right. My mistake. Changed it. – TurBas Feb 09 '11 at 12:22
  • Thanks u r awesome, one issue if someclass has not some of the properties which are in dictionary. It should match only properties exists in someclass and skip other, as of now I used try catch any better option for this? – Sunil Chaudhary Jun 29 '18 at 06:43
5

Building on Matías Fidemraizer's answer, here is a version that supports binding to object properties other than strings.

using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace WebOpsApi.Shared.Helpers
{
    public static class MappingExtension
    {
        public static T ToObject<T>(this IDictionary<string, object> source)
            where T : class, new()
        {
            var someObject = new T();
            var someObjectType = someObject.GetType();

            foreach (var item in source)
            {
                var key = char.ToUpper(item.Key[0]) + item.Key.Substring(1);
                var targetProperty = someObjectType.GetProperty(key);

                //edited this line
                if (targetProperty.PropertyType == item.Value.GetType())
                {
                    targetProperty.SetValue(someObject, item.Value);
                }
                else
                {

                    var parseMethod = targetProperty.PropertyType.GetMethod("TryParse",
                        BindingFlags.Public | BindingFlags.Static, null,
                        new[] {typeof (string), targetProperty.PropertyType.MakeByRefType()}, null);
                    
                    if (parseMethod != null)
                    {
                        var parameters = new[] { item.Value, null };
                        var success = (bool)parseMethod.Invoke(null, parameters);
                        if (success)
                        {
                            targetProperty.SetValue(someObject, parameters[1]);
                        }

                    }
                }
            }

            return someObject;
        }

        public static IDictionary<string, object> AsDictionary(this object source, BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
        {
            return source.GetType().GetProperties(bindingAttr).ToDictionary
            (
                propInfo => propInfo.Name,
                propInfo => propInfo.GetValue(source, null)
            );
        }
    }
}
Daniel Lewis
  • 51
  • 1
  • 2
5

Reflection can take you from an object to a dictionary by iterating over the properties.

To go the other way, you'll have to use a dynamic ExpandoObject (which, in fact, already inherits from IDictionary, and so has done this for you) in C#, unless you can infer the type from the collection of entries in the dictionary somehow.

So, if you're in .NET 4.0 land, use an ExpandoObject, otherwise you've got a lot of work to do...

Massif
  • 4,327
  • 23
  • 25
3
public class SimpleObjectDictionaryMapper<TObject>
{
    public static TObject GetObject(IDictionary<string, object> d)
    {
        PropertyInfo[] props = typeof(TObject).GetProperties();
        TObject res = Activator.CreateInstance<TObject>();
        for (int i = 0; i < props.Length; i++)
        {
            if (props[i].CanWrite && d.ContainsKey(props[i].Name))
            {
                props[i].SetValue(res, d[props[i].Name], null);
            }
        }
        return res;
    }

    public static IDictionary<string, object> GetDictionary(TObject o)
    {
        IDictionary<string, object> res = new Dictionary<string, object>();
        PropertyInfo[] props = typeof(TObject).GetProperties();
        for (int i = 0; i < props.Length; i++)
        {
            if (props[i].CanRead)
            {
                res.Add(props[i].Name, props[i].GetValue(o, null));
            }
        }
        return res;
    }
}
Saw
  • 6,199
  • 11
  • 53
  • 104
1

If you are using Asp.Net MVC, then take a look at:

public static RouteValueDictionary AnonymousObjectToHtmlAttributes(object htmlAttributes);

which is a static public method on the System.Web.Mvc.HtmlHelper class.

magritte
  • 7,396
  • 10
  • 59
  • 79
  • I won't use it as it replaces "_" with "-". And you need MVC. Use the pure Routing class: new RouteValueDictionary(objectHere); – regisbsb Feb 05 '14 at 10:52
0
    public Dictionary<string, object> ToDictionary<T>(string key, T value)
    {
        try
        {
            var payload = new Dictionary<string, object>
            {
                { key, value }
            }; 
        } catch (Exception e)
        {
            return null;
        }
    }

    public T FromDictionary<T>(Dictionary<string, object> payload, string key)
    {
        try
        {
            JObject jObject = (JObject) payload[key];
            T t = jObject.ToObject<T>();
            return (t);
        }
        catch(Exception e) {
            return default(T);
        }
    }
  • 1
    While this code may answer the question, providing additional context regarding why and/or how this code answers the question improves its long-term value. – baikho Jul 28 '18 at 10:52