【问题标题】:Converting hierarchy of Dictionary<string, object> to nested class将 Dictionary<string, object> 的层次结构转换为嵌套类
【发布时间】:2016-08-15 14:35:57
【问题描述】:

我有一个基类,其中包含一系列作为公共成员的类。我正在尝试将 Dictionary 转换为此类。

知道如何克服这个异常吗?当基类包含原始类型而不是我创建的类型(或我创建的类型列表)时,此代码似乎工作得很好。

异常输出:

================================================================================
 = Exception Type: System.InvalidCastException
 = Exception Dat System.Collections.ListDictionaryInternal
 = Inner Exception:
 = Exception Message: Object must implement IConvertible.
 = Exception Source: mscorlib
 = Exception StackTrace:    at System.Convert.ChangeType(Object value, Type conversionType, IFormatProvider provider)
   at System.Convert.ChangeType(Object value, Type conversionType)
   at sandbox.Program.DictToObject[T](IDictionary`2 dict) in D:\Code\Misc\Sandbox\sandbox\Program.cs:line 91
   at sandbox.Program.Main(String[] args) in D:\Code\Misc\Sandbox\sandbox\Program.cs:line 48
================================================================================

代码:

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

namespace sandbox
{
    public class BaseClass
    {
        public int SomeInt { get; set; }
        public string SomeString { get; set; }
        public SubClass ScSingleton { get; set; }
        public List<SubClass> ScList { get; set; }
    }

    public class SubClass
    {
        public int AnotherInt { get; set; }
        public string AnotherString { get; set; }
    }

    public partial class Program
    {
        static void Main(string[] args)
        {
            try
            {
                Dictionary<string, object> outer = new Dictionary<string, object>();
                outer.Add("SomeInt", 5);
                outer.Add("SomeString", "foo");

                Dictionary<string, object> scSingle = new Dictionary<string, object>();
                scSingle.Add("AnotherInt", 10);
                scSingle.Add("AnotherString", "bar");
                outer.Add("ScSingleton", scSingle);

                List<Dictionary<string, object>> scList = new List<Dictionary<string, object>>();
                scList.Add(scSingle);
                outer.Add("ScList", scList);

                BaseClass b = DictToObject<BaseClass>(outer);
            }
            catch (Exception e)
            {
                PrintException(e);
            }
            finally
            {
                Console.WriteLine("");
                Console.Write("Press ENTER to exit.");
                Console.ReadLine();
            }

            return;
        }

        public static T DictToObject<T>(IDictionary<string, object> dict) where T : new()
        {
            T t = new T();
            PropertyInfo[] properties = t.GetType().GetProperties();

            foreach (KeyValuePair<string, object> curr in dict)
            {
                if (String.IsNullOrEmpty(curr.Key)) continue;
                if (curr.Value == null) continue;

                Type valType = null;
                Type newType = null;
                PropertyInfo currProperty = null;
                foreach (PropertyInfo p in properties)
                {
                    if (String.IsNullOrEmpty(p.Name)) continue;

                    if (String.Compare(p.Name.ToLower(), curr.Key.ToLower()) == 0)
                    {
                        valType = t.GetType().GetProperty(p.Name).PropertyType;
                        newType = Nullable.GetUnderlyingType(valType) ?? valType;
                        currProperty = p;
                        break;
                    }
                }

                object newVal = Convert.ChangeType(curr.Value, newType);
                t.GetType().GetProperty(currProperty.Name).SetValue(t, newVal);
            }

            return t;
        }

        static void PrintException(Exception e)
        {
            Console.WriteLine("================================================================================");
            Console.WriteLine(" = Exception Type: " + e.GetType().ToString());
            Console.WriteLine(" = Exception Dat " + e.Data);
            Console.WriteLine(" = Inner Exception: " + e.InnerException);
            Console.WriteLine(" = Exception Message: " + e.Message);
            Console.WriteLine(" = Exception Source: " + e.Source);
            Console.WriteLine(" = Exception StackTrace: " + e.StackTrace);
            Console.WriteLine("================================================================================");
        }
    }
}

【问题讨论】:

  • 你可以在你的类型中实现 IConvertible
  • 嗨 Stuart,如果你展示一个工作示例,我会立即接受。谢谢!
  • 啊,我看到您正在从 object 转换为您的自定义类型,并且无法更改 object 的 IConvertible 实现。

标签: c# reflection type-conversion system.reflection


【解决方案1】:

如果你坚持走黑色反射魔法的道路,你可以使用类似下面的东西来完成你正在寻找的东西(丑陋而低效,可以美化和优化):

interface ICollectionBuilder
{
    object Build(IList dictionaries);
}

interface IDictionaryConverter
{
    object Convert(IDictionary<string, object> dict);
}

class DictionaryConerter<T> : IDictionaryConverter where T : new()
{
    public object Convert(IDictionary<string, object> dict)
    {
        return ConvertTyped(dict);
    }

    public T ConvertTyped(IDictionary<string, object> dict)
    {
        T t = new T();
        PropertyInfo[] properties = t.GetType().GetProperties();

        foreach (KeyValuePair<string, object> curr in dict)
        {
            if (String.IsNullOrEmpty(curr.Key)) continue;
            if (curr.Value == null) continue;

            Type valType = null;
            Type newType = null;
            PropertyInfo currProperty = null;
            foreach (PropertyInfo p in properties)
            {
                if (String.IsNullOrEmpty(p.Name)) continue;

                if (String.Compare(p.Name.ToLower(), curr.Key.ToLower()) == 0)
                {
                    valType = t.GetType().GetProperty(p.Name).PropertyType;
                    newType = Nullable.GetUnderlyingType(valType) ?? valType;
                    currProperty = p;
                    break;
                }
            }

            //you don't have to cast the object here, PropertyInfo.SetValue will accept it "as is":
            object newVal = curr.Value;             

            IDictionary<string, object> curDict = curr.Value as IDictionary<string, object>;
            IList curList = curr.Value as IList;
            if (curDict != null && newType.GetConstructor(Type.EmptyTypes) != null)
            {
                newVal = ((IDictionaryConverter)Activator.CreateInstance(typeof(DictionaryConerter<>).MakeGenericType(newType))).Convert(curDict);
            }
            else if (
                curList != null &&
                curList.OfType<IDictionary<string,object>>().Any() &&
                newType.IsGenericType &&
                newType.GetGenericTypeDefinition() == typeof(List<>) &&
                newType.GetGenericArguments()[0].GetConstructor(Type.EmptyTypes) != null)
            {
                newVal = ((ICollectionBuilder)Activator.CreateInstance(typeof(CollectionBuilder<>).MakeGenericType(newType.GetGenericArguments()[0]))).Build(curList);
            }

            t.GetType().GetProperty(currProperty.Name).SetValue(t, newVal);
        }

        return t;
    }
}

class CollectionBuilder<T> : ICollectionBuilder where T : new()
{
    public object Build(IList dictionaries)
    {
        DictionaryConerter<T> dictConverter = new DictionaryConerter<T>();
        List<T> list = dictionaries
            .OfType<IDictionary<string,object>>()
            .Select(dict => dictConverter.ConvertTyped(dict))
            .ToList();

        return list;
    }
}

及用法:

BaseClass b = new DictionaryConerter<BaseClass>().ConvertTyped(outer);

当然,你可以在Dictionary&lt;Type, ICollectionBuilder&gt;Dictionary&lt;Type, IDictionaryConverter&gt;中缓存ICollectionBuilderIDictionaryConverter的实例。

【讨论】:

  • 感谢 Galenus,这对 List 不起作用,但它会引发异常 (System.InvalidCastException)。
  • 正如我在答案中所写,您可以轻松地将此代码扩展为集合。要我提供完整的解决方案吗?
  • 你有一些病态的巫毒,我的朋友。谢谢你。
  • 顺便说一句,您将如何扩展 ArrayList?
  • ArrayList 如果将字典放入其中,将难以恢复,除非您以某种方式保留类型信息,例如,在专用键下(如 Json.Net 中的“$type”)。跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多