将对象映射到字典,反之亦然


90

是否有任何优雅的快速方法将对象映射到字典,反之亦然?

例:

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

变成

SomeClass b = new SomeClass();
b.Id=1;
b.Name="Ahmad";
// ..........

最快的方法是像protobuf这样的代码生成...我使用表达式树来尽可能地避免反射
Konrad

下面的所有自编码答案均不支持深度转换,因此在现实生活中的应用中将不起作用。您应该使用Earerplates建议的类似Newtonsoft的库
Cesar

Answers:


175

在两种扩展方法中使用一些反射和泛型即可实现。

是的,其他人基本上都采用了相同的解决方案,但这使用了较少的反射,这在性能上和可读性上都更高:

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();
    }
}

我喜欢该实现,实际上已经开始使用它,但是您对性能有任何反馈吗?我知道在表现方面反射不是最大的吗?你有什么意见吗?
nolimit

@nolimit实际上我不知道它的实际性能,但是我想它还不错(当然,比避免反射更糟)。实际上,有什么选择?根据项目的需求,也许还有其他选择,例如使用动态类型,它比反射快得多……谁知道!:)
马蒂亚斯Fidemraizer

在我的情况下,替代方案就像通过分配来构建对象一样简单,但是我正在寻找一种简洁而廉价的方法来构建对象。话虽如此,我发现对我来说意味着使用那段代码并不太昂贵。
nolimit

@nolimit是的,似乎没有问题。只是使用正确的工具来解决所需的问题:D在您的情况下,它应该像魅力一样工作。我相信我可以在代码中进行调整!
马蒂亚斯Fidemraizer

@nolimit检查第一个方法是否不再为每个属性GetType()调用它someObject
马蒂亚斯Fidemraizer

30

首先使用Newtonsoft将Dictionary转换为JSON字符串。

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

然后将JSON字符串反序列化为您的对象

var myobject = JsonConvert.DeserializeObject<AOCAdvancedSettings>(json);

1
简单而富有创意!
kamalpreet

1
请谨慎使用此方法。如果用于许多(数百)词典,它将使您的应用程序停滞不前。
amackay11

12

似乎反射只在这里有用。.我已经做了一个小例子,将对象转换为字典,反之亦然:

[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;
}

这不支持嵌套。
塞萨尔

10

我强烈推荐Castle DictionaryAdapter,它很容易成为该项目保存最完好的秘密之一。您只需要用所需的属性定义一个接口,并且在一行代码中,适配器将生成一个实现,将其实例化,并将其值与您传入的字典进行同步。一个网络项目:

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

请注意,我不需要创建一个实现IAppSettings的类-适配器可以即时进行。另外,尽管在这种情况下我只是阅读,但从理论上讲,如果我要在appSettings上设置属性值,那么适配器将使基础词典与这些更改保持同步。


2
我猜这“最保守的秘密”是一个孤独的死亡。上面的Castle DictionaryAdapter链接以及castleproject.org上的“ DictionaryAdapter”下载链接都转到404页:(
Shiva

1
没有!它仍然在Castle.Core中。我已经修复了链接。
Gaspar Nagy 2013年

卡斯尔(Castle)是一个很棒的图书馆,无论如何都被忽略了
bikeman868

5

通过遍历属性,反射可以将您从一个对象带到一个字典。

为了走另一条路,你必须使用动态ExpandoObject在C#中(其中,事实上,已经继承IDictionary的,所以做了这个给你),除非你可以从项目的集合推断类型字典不知何故。

因此,如果您使用的是.NET 4.0,请使用ExpandoObject,否则您需要做很多工作...


4

我认为您应该使用反思。像这样:

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;
}

它使用您的字典并循环遍历并设置值。您应该做得更好,但这是一个开始。您应该这样称呼它:

SomeClass someClass = ConvertDictionaryTo<SomeClass>(a);

如果可以使用“新”通用约束,则要使用什么激活剂?:)
马蒂亚斯Fidemraizer

@MatíasFidemraizer你是绝对正确的。我的错。改了
TurBas 2011年

非常感谢,如果someclass没有字典中的某些属性,则会出现一个问题。它应该只匹配某类中存在的属性,并跳过其他属性,截至目前,我使用try catch对此是否有更好的选择?
Sunil Chaudhary

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;
    }
}

2

该版本以MatíasFidemraizer的答案为基础,该版本支持绑定到除字符串以外的对象属性。

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);


                if (targetProperty.PropertyType == typeof (string))
                {
                    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)
            );
        }
    }
}

1

如果您使用的是Asp.Net MVC,请看一下:

public static RouteValueDictionary AnonymousObjectToHtmlAttributes(object htmlAttributes);

这是System.Web.Mvc.HtmlHelper类上的静态公共方法。


我不会使用它,因为它将“ _”替换为“-”。而且您需要MVC。使用纯路由类:new RouteValueDictionary(objectHere);
regisbsb 2014年

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);
        }
    }

尽管此代码可以回答问题,但提供有关此代码为何和/或如何回答问题的其他上下文,可以改善其长期价值。
baikho
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.