Convert Dictionary To Anonymous Object?

后端 未结 7 1349
挽巷
挽巷 2020-11-27 16:33

First, and to make things clearer I\'ll explain my scenario from the top:

I have a method which has the following signature:

public virtual void Send         


        
相关标签:
7条回答
  • 2020-11-27 16:44

    Slightly more modular version of svick's answer, using a couple extension methods:

    public static class Extensions
    {
        public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> items)
        {
            foreach (var item in items)
            {
                collection.Add(item);
            }
        }
    
        public static dynamic ToDynamicObject(this IDictionary<string, object> source)
        {
            ICollection<KeyValuePair<string, object>> someObject = new ExpandoObject();
            someObject.AddRange(source);
            return someObject;
        }
    }
    
    0 讨论(0)
  • 2020-11-27 16:45

    The credit here goes to the accepted answer. Adding this because I wanted to turn a List< Dictionary< string,object >> into a List< dynamic>. The purpose is for pulling records from a database table. Here is what I did.

        public static List<dynamic> ListDictionaryToListDynamic(List<Dictionary<string,object>> dbRecords)
        {
            var eRecords = new List<dynamic>();
            foreach (var record in dbRecords)
            {
                var eRecord = new ExpandoObject() as IDictionary<string, object>;
                foreach (var kvp in record)
                {
                    eRecord.Add(kvp);
                }
                eRecords.Add(eRecord);
            }
            return eRecords;
        }
    
    0 讨论(0)
  • 2020-11-27 16:46

    If you want to convert Dictionary<string, object> To Anonymous System.Object. You can use this method:

    public static object FromDictToAnonymousObj<TValue>(IDictionary<string, TValue> dict)
    {
        var types = new Type[dict.Count];
    
        for (int i = 0; i < types.Length; i++)
        {
            types[i] = typeof(TValue);
        }
    
        // dictionaries don't have an order, so we force an order based
        // on the Key
        var ordered = dict.OrderBy(x => x.Key).ToArray();
    
        string[] names = Array.ConvertAll(ordered, x => x.Key);
    
        Type type = AnonymousType.CreateType(types, names);
    
        object[] values = Array.ConvertAll(ordered, x => (object)x.Value);
    
        object obj = type.GetConstructor(types).Invoke(values);
    
        return obj;
    }
    

    like this:

    var dict = new Dictionary<string, string>
    {
        {"Id", "1"},
        {"Title", "My title"},
        {"Description", "Blah blah blah"},
    };
    
    object obj1 = FromDictToAnonymousObj(dict);
    

    to obtain your object. Where AnonymousType class code is:

    using System;
    using System.Collections.Concurrent;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Reflection;
    using System.Reflection.Emit;
    using System.Runtime.CompilerServices;
    using System.Text;
    using System.Threading;
    
    /// <summary>
    /// The code generated should be nearly equal to the one generated by
    /// csc 12.0.31101.0 when compiling with /optimize+ /debug-. The main
    /// difference is in the GetHashCode() (the base init_hash used is 
    /// compiler-dependant) and in the maxstack of the generated methods.
    /// Note that Roslyn (at least the one present at 
    /// tryroslyn.azurewebsites.net) generates different code for anonymous
    /// types.
    /// </summary>
    public static class AnonymousType
    {
        private static readonly ConcurrentDictionary<string, Type> GeneratedTypes = new ConcurrentDictionary<string, Type>();
    
        private static readonly AssemblyBuilder AssemblyBuilder;
        private static readonly ModuleBuilder ModuleBuilder;
        private static readonly string FileName;
    
        // Some objects we cache
        private static readonly CustomAttributeBuilder CompilerGeneratedAttributeBuilder = new CustomAttributeBuilder(typeof(CompilerGeneratedAttribute).GetConstructor(Type.EmptyTypes), new object[0]);
        private static readonly CustomAttributeBuilder DebuggerBrowsableAttributeBuilder = new CustomAttributeBuilder(typeof(DebuggerBrowsableAttribute).GetConstructor(new[] { typeof(DebuggerBrowsableState) }), new object[] { DebuggerBrowsableState.Never });
        private static readonly CustomAttributeBuilder DebuggerHiddenAttributeBuilder = new CustomAttributeBuilder(typeof(DebuggerHiddenAttribute).GetConstructor(Type.EmptyTypes), new object[0]);
    
        private static readonly ConstructorInfo ObjectCtor = typeof(object).GetConstructor(Type.EmptyTypes);
        private static readonly MethodInfo ObjectToString = typeof(object).GetMethod("ToString", BindingFlags.Instance | BindingFlags.Public, null, Type.EmptyTypes, null);
    
        private static readonly ConstructorInfo StringBuilderCtor = typeof(StringBuilder).GetConstructor(Type.EmptyTypes);
        private static readonly MethodInfo StringBuilderAppendString = typeof(StringBuilder).GetMethod("Append", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(string) }, null);
        private static readonly MethodInfo StringBuilderAppendObject = typeof(StringBuilder).GetMethod("Append", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(object) }, null);
    
        private static readonly Type EqualityComparer = typeof(EqualityComparer<>);
        private static readonly Type EqualityComparerGenericArgument = EqualityComparer.GetGenericArguments()[0];
        private static readonly MethodInfo EqualityComparerDefault = EqualityComparer.GetMethod("get_Default", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null);
        private static readonly MethodInfo EqualityComparerEquals = EqualityComparer.GetMethod("Equals", BindingFlags.Instance | BindingFlags.Public, null, new[] { EqualityComparerGenericArgument, EqualityComparerGenericArgument }, null);
        private static readonly MethodInfo EqualityComparerGetHashCode = EqualityComparer.GetMethod("GetHashCode", BindingFlags.Instance | BindingFlags.Public, null, new[] { EqualityComparerGenericArgument }, null);
    
        private static int Index = -1;
    
        static AnonymousType()
        {
            var assemblyName = new AssemblyName("AnonymousTypes");
    
            FileName = assemblyName.Name + ".dll";
    
            AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
            ModuleBuilder = AssemblyBuilder.DefineDynamicModule("AnonymousTypes", FileName);
        }
    
        public static void Dump()
        {
            AssemblyBuilder.Save(FileName);
        }
    
        /// <summary>
        /// 
        /// </summary>
        /// <param name="types"></param>
        /// <param name="names"></param>
        /// <returns></returns>
        public static Type CreateType(Type[] types, string[] names)
        {
            if (types == null)
            {
                throw new ArgumentNullException("types");
            }
    
            if (names == null)
            {
                throw new ArgumentNullException("names");
            }
    
            if (types.Length != names.Length)
            {
                throw new ArgumentException("names");
            }
    
            // Anonymous classes are generics based. The generic classes
            // are distinguished by number of parameters and name of 
            // parameters. The specific types of the parameters are the 
            // generic arguments. We recreate this by creating a fullName
            // composed of all the property names, separated by a "|"
            string fullName = string.Join("|", names.Select(x => Escape(x)));
    
            Type type;
    
            if (!GeneratedTypes.TryGetValue(fullName, out type))
            {
                // We create only a single class at a time, through this lock
                // Note that this is a variant of the double-checked locking.
                // It is safe because we are using a thread safe class.
                lock (GeneratedTypes)
                {
                    if (!GeneratedTypes.TryGetValue(fullName, out type))
                    {
                        int index = Interlocked.Increment(ref Index);
    
                        string name = names.Length != 0 ? string.Format("<>f__AnonymousType{0}`{1}", index, names.Length) : string.Format("<>f__AnonymousType{0}", index);
                        TypeBuilder tb = ModuleBuilder.DefineType(name, TypeAttributes.AnsiClass | TypeAttributes.Class | TypeAttributes.AutoLayout | TypeAttributes.NotPublic | TypeAttributes.Sealed | TypeAttributes.BeforeFieldInit);
                        tb.SetCustomAttribute(CompilerGeneratedAttributeBuilder);
    
                        GenericTypeParameterBuilder[] generics = null;
    
                        if (names.Length != 0)
                        {
                            string[] genericNames = Array.ConvertAll(names, x => string.Format("<{0}>j__TPar", x));
                            generics = tb.DefineGenericParameters(genericNames);
                        }
                        else
                        {
                            generics = new GenericTypeParameterBuilder[0];
                        }
    
                        // .ctor
                        ConstructorBuilder constructor = tb.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig, CallingConventions.HasThis, generics);
                        constructor.SetCustomAttribute(DebuggerHiddenAttributeBuilder);
                        ILGenerator ilgeneratorConstructor = constructor.GetILGenerator();
                        ilgeneratorConstructor.Emit(OpCodes.Ldarg_0);
                        ilgeneratorConstructor.Emit(OpCodes.Call, ObjectCtor);
    
                        var fields = new FieldBuilder[names.Length];
    
                        // There are two for cycles because we want to have
                        // all the getter methods before all the other 
                        // methods
                        for (int i = 0; i < names.Length; i++)
                        {
                            // field
                            fields[i] = tb.DefineField(string.Format("<{0}>i__Field", names[i]), generics[i], FieldAttributes.Private | FieldAttributes.InitOnly);
                            fields[i].SetCustomAttribute(DebuggerBrowsableAttributeBuilder);
    
                            // .ctor
                            constructor.DefineParameter(i + 1, ParameterAttributes.None, names[i]);
                            ilgeneratorConstructor.Emit(OpCodes.Ldarg_0);
    
                            if (i == 0)
                            {
                                ilgeneratorConstructor.Emit(OpCodes.Ldarg_1);
                            }
                            else if (i == 1)
                            {
                                ilgeneratorConstructor.Emit(OpCodes.Ldarg_2);
                            }
                            else if (i == 2)
                            {
                                ilgeneratorConstructor.Emit(OpCodes.Ldarg_3);
                            }
                            else if (i < 255)
                            {
                                ilgeneratorConstructor.Emit(OpCodes.Ldarg_S, (byte)(i + 1));
                            }
                            else
                            {
                                // Ldarg uses a ushort, but the Emit only
                                // accepts short, so we use a unchecked(...),
                                // cast to short and let the CLR interpret it
                                // as ushort
                                ilgeneratorConstructor.Emit(OpCodes.Ldarg, unchecked((short)(i + 1)));
                            }
    
                            ilgeneratorConstructor.Emit(OpCodes.Stfld, fields[i]);
    
                            // getter
                            MethodBuilder getter = tb.DefineMethod(string.Format("get_{0}", names[i]), MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName, CallingConventions.HasThis, generics[i], Type.EmptyTypes);
                            ILGenerator ilgeneratorGetter = getter.GetILGenerator();
                            ilgeneratorGetter.Emit(OpCodes.Ldarg_0);
                            ilgeneratorGetter.Emit(OpCodes.Ldfld, fields[i]);
                            ilgeneratorGetter.Emit(OpCodes.Ret);
    
                            PropertyBuilder property = tb.DefineProperty(names[i], PropertyAttributes.None, CallingConventions.HasThis, generics[i], Type.EmptyTypes);
                            property.SetGetMethod(getter);
                        }
    
                        // ToString()
                        MethodBuilder toString = tb.DefineMethod("ToString", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, CallingConventions.HasThis, typeof(string), Type.EmptyTypes);
                        toString.SetCustomAttribute(DebuggerHiddenAttributeBuilder);
                        ILGenerator ilgeneratorToString = toString.GetILGenerator();
    
                        ilgeneratorToString.DeclareLocal(typeof(StringBuilder));
    
                        ilgeneratorToString.Emit(OpCodes.Newobj, StringBuilderCtor);
                        ilgeneratorToString.Emit(OpCodes.Stloc_0);
    
                        // Equals
                        MethodBuilder equals = tb.DefineMethod("Equals", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, CallingConventions.HasThis, typeof(bool), new[] { typeof(object) });
                        equals.SetCustomAttribute(DebuggerHiddenAttributeBuilder);
                        equals.DefineParameter(1, ParameterAttributes.None, "value");
                        ILGenerator ilgeneratorEquals = equals.GetILGenerator();
                        ilgeneratorEquals.DeclareLocal(tb);
    
                        ilgeneratorEquals.Emit(OpCodes.Ldarg_1);
                        ilgeneratorEquals.Emit(OpCodes.Isinst, tb);
                        ilgeneratorEquals.Emit(OpCodes.Stloc_0);
                        ilgeneratorEquals.Emit(OpCodes.Ldloc_0);
    
                        Label equalsLabel = ilgeneratorEquals.DefineLabel();
    
                        // GetHashCode()
                        MethodBuilder getHashCode = tb.DefineMethod("GetHashCode", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, CallingConventions.HasThis, typeof(int), Type.EmptyTypes);
                        getHashCode.SetCustomAttribute(DebuggerHiddenAttributeBuilder);
                        ILGenerator ilgeneratorGetHashCode = getHashCode.GetILGenerator();
                        ilgeneratorGetHashCode.DeclareLocal(typeof(int));
    
                        if (names.Length == 0)
                        {
                            ilgeneratorGetHashCode.Emit(OpCodes.Ldc_I4_0);
                        }
                        else
                        {
                            // As done by Roslyn
                            // Note that initHash can vary, because
                            // string.GetHashCode() isn't "stable" for 
                            // different compilation of the code
                            int initHash = 0;
    
                            for (int i = 0; i < names.Length; i++)
                            {
                                initHash = unchecked(initHash * (-1521134295) + fields[i].Name.GetHashCode());
                            }
    
                            // Note that the CSC seems to generate a 
                            // different seed for every anonymous class
                            ilgeneratorGetHashCode.Emit(OpCodes.Ldc_I4, initHash);
                        }
    
                        for (int i = 0; i < names.Length; i++)
                        {
                            // Equals()
                            Type equalityComparerT = EqualityComparer.MakeGenericType(generics[i]);
                            MethodInfo equalityComparerTDefault = TypeBuilder.GetMethod(equalityComparerT, EqualityComparerDefault);
                            MethodInfo equalityComparerTEquals = TypeBuilder.GetMethod(equalityComparerT, EqualityComparerEquals);
    
                            ilgeneratorEquals.Emit(OpCodes.Brfalse_S, equalsLabel);
                            ilgeneratorEquals.Emit(OpCodes.Call, equalityComparerTDefault);
                            ilgeneratorEquals.Emit(OpCodes.Ldarg_0);
                            ilgeneratorEquals.Emit(OpCodes.Ldfld, fields[i]);
                            ilgeneratorEquals.Emit(OpCodes.Ldloc_0);
                            ilgeneratorEquals.Emit(OpCodes.Ldfld, fields[i]);
                            ilgeneratorEquals.Emit(OpCodes.Callvirt, equalityComparerTEquals);
    
                            // GetHashCode();
                            MethodInfo EqualityComparerTGetHashCode = TypeBuilder.GetMethod(equalityComparerT, EqualityComparerGetHashCode);
    
                            ilgeneratorGetHashCode.Emit(OpCodes.Stloc_0);
                            ilgeneratorGetHashCode.Emit(OpCodes.Ldc_I4, -1521134295);
                            ilgeneratorGetHashCode.Emit(OpCodes.Ldloc_0);
                            ilgeneratorGetHashCode.Emit(OpCodes.Mul);
                            ilgeneratorGetHashCode.Emit(OpCodes.Call, EqualityComparerDefault);
                            ilgeneratorGetHashCode.Emit(OpCodes.Ldarg_0);
                            ilgeneratorGetHashCode.Emit(OpCodes.Ldfld, fields[i]);
                            ilgeneratorGetHashCode.Emit(OpCodes.Callvirt, EqualityComparerGetHashCode);
                            ilgeneratorGetHashCode.Emit(OpCodes.Add);
    
                            // ToString()
                            ilgeneratorToString.Emit(OpCodes.Ldloc_0);
                            ilgeneratorToString.Emit(OpCodes.Ldstr, i == 0 ? string.Format("{{ {0} = ", names[i]) : string.Format(", {0} = ", names[i]));
                            ilgeneratorToString.Emit(OpCodes.Callvirt, StringBuilderAppendString);
                            ilgeneratorToString.Emit(OpCodes.Pop);
                            ilgeneratorToString.Emit(OpCodes.Ldloc_0);
                            ilgeneratorToString.Emit(OpCodes.Ldarg_0);
                            ilgeneratorToString.Emit(OpCodes.Ldfld, fields[i]);
                            ilgeneratorToString.Emit(OpCodes.Box, generics[i]);
                            ilgeneratorToString.Emit(OpCodes.Callvirt, StringBuilderAppendObject);
                            ilgeneratorToString.Emit(OpCodes.Pop);
                        }
    
                        // .ctor
                        ilgeneratorConstructor.Emit(OpCodes.Ret);
    
                        // Equals()
                        if (names.Length == 0)
                        {
                            ilgeneratorEquals.Emit(OpCodes.Ldnull);
                            ilgeneratorEquals.Emit(OpCodes.Ceq);
                            ilgeneratorEquals.Emit(OpCodes.Ldc_I4_0);
                            ilgeneratorEquals.Emit(OpCodes.Ceq);
                        }
                        else
                        {
                            ilgeneratorEquals.Emit(OpCodes.Ret);
                            ilgeneratorEquals.MarkLabel(equalsLabel);
                            ilgeneratorEquals.Emit(OpCodes.Ldc_I4_0);
                        }
    
                        ilgeneratorEquals.Emit(OpCodes.Ret);
    
                        // GetHashCode()
                        ilgeneratorGetHashCode.Emit(OpCodes.Stloc_0);
                        ilgeneratorGetHashCode.Emit(OpCodes.Ldloc_0);
                        ilgeneratorGetHashCode.Emit(OpCodes.Ret);
    
                        // ToString()
                        ilgeneratorToString.Emit(OpCodes.Ldloc_0);
                        ilgeneratorToString.Emit(OpCodes.Ldstr, names.Length == 0 ? "{ }" : " }");
                        ilgeneratorToString.Emit(OpCodes.Callvirt, StringBuilderAppendString);
                        ilgeneratorToString.Emit(OpCodes.Pop);
                        ilgeneratorToString.Emit(OpCodes.Ldloc_0);
                        ilgeneratorToString.Emit(OpCodes.Callvirt, ObjectToString);
                        ilgeneratorToString.Emit(OpCodes.Ret);
    
                        type = tb.CreateType();
    
                        type = GeneratedTypes.GetOrAdd(fullName, type);
                    }
                }
            }
    
            if (types.Length != 0)
            {
                type = type.MakeGenericType(types);
            }
    
            return type;
        }
    
        private static string Escape(string str)
        {
            // We escape the \ with \\, so that we can safely escape the
            // "|" (that we use as a separator) with "\|"
            str = str.Replace(@"\", @"\\");
            str = str.Replace(@"|", @"\|");
            return str;
        }
    }
    

    Reference: https://stackoverflow.com/a/29428640/2073920

    0 讨论(0)
  • 2020-11-27 16:54

    If you really want to convert the dictionary to an object that has the items of the dictionary as properties, you can use ExpandoObject:

    var dict = new Dictionary<string, object> { { "Property", "foo" } };
    var eo = new ExpandoObject();
    var eoColl = (ICollection<KeyValuePair<string, object>>)eo;
    
    foreach (var kvp in dict)
    {
        eoColl.Add(kvp);
    }
    
    dynamic eoDynamic = eo;
    
    string value = eoDynamic.Property;
    

    But I'm not sure how is doing that going to help you.

    0 讨论(0)
  • 2020-11-27 16:54

    If you have a class you want to covert the dictionary too, you can use the following to convert a dictionary to an object of that class:

    Example class:

    public class Properties1
    {
        public string Property { get; set; }
    }
    

    The solution:

    JavaScriptSerializer serializer = new JavaScriptSerializer();
    Dictionary<string, object> dict = new Dictionary<string, object> { { "Property", "foo" } };
    Properties1 properties = serializer.ConvertToType<Properties1>(dict);
    string value = properties.Property;
    

    You could also use a method like this to build the object from the dictionary, obviously this also requires you to have a class.

    private static T DictionaryToObject<T>(IDictionary<string, object> dict) where T : new()
    {
        T t = new T();
        PropertyInfo[] properties = t.GetType().GetProperties();
    
        foreach (PropertyInfo property in properties)
        {
            if (!dict.Any(x => x.Key.Equals(property.Name, 
                StringComparison.InvariantCultureIgnoreCase)))
                continue;
            KeyValuePair<string, object> item = dict.First(x => x.Key.Equals(property.Name,
                StringComparison.InvariantCultureIgnoreCase));
            Type tPropertyType = t.GetType().GetProperty(property.Name).PropertyType;
            Type newT = Nullable.GetUnderlyingType(tPropertyType) ?? tPropertyType;
            object newA = Convert.ChangeType(item.Value, newT);
            t.GetType().GetProperty(property.Name).SetValue(t, newA, null);
        }
        return t;
    }
    

    However if you do not have the class you can create a dynamic object from a dictionary like this:

    private static dynamic DictionaryToObject(Dictionary<string, object> dict)
    {
        IDictionary<string, object> eo = (IDictionary<string, object>)new ExpandoObject();
        foreach (KeyValuePair<string, object> kvp in dict)
        {
            eo.Add(kvp);
        }
        return eo;
    }
    

    You can use it like this:

    Dictionary<string, object> dict = new Dictionary<string, object> {{ "Property", "foo" }};
    dynamic properties = DictionaryToObject(dict);
    string value = properties.Property;
    
    0 讨论(0)
  • 2020-11-27 16:55

    Anonymous objects are one that generated for you by compiler. You cannot generate dynamically create one. On the other hand you can emit such object, but I really do not think this is good idea.

    May be you can try dynamic objects? The result will be an object with all properties you need.

    0 讨论(0)
提交回复
热议问题