Convert Dictionary<string, object> To Anonymous Object?
Solution 1
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;
Solution 2
I tried to do this in one statement with a reduce function (Aggregate in Linq). The code below does the same as the accepted answer:
var dict = new Dictionary<string, object> { { "Property", "foo" } };
dynamic eo = dict.Aggregate(new ExpandoObject() as IDictionary<string, Object>,
(a, p) => { a.Add(p); return a; });
string value = eo.Property;
Solution 3
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
Solution 4
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;
Solution 5
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;
}
}
Kassem
Updated on June 24, 2021Comments
-
Kassem almost 3 years
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 SendEmail(String from, List<String> recepients, Object model)
What I want to do is generate an anonymous object which has the properties of the model object along with the first two parameters as well. Flattening the model object into a PropertyInfo[] is very straightforward. Accordingly, I thought of creating a Dictionary which would hold the PropertyInfo's and the first two params, and then be converted into the anonymous object where the key is the name of the property and the value is the actual value of the property.
Is that possible? Any other suggestions?
-
Kassem over 12 yearsYup a dynamic object would be perfect. Care to provide an example?
-
Chris Marisic about 12 yearsI don't think this is an accurate statement, you can even create true CLR types at runtime I can't imagine why you couldn't create an anonymous type at runtime.
-
Mike Chaliy about 12 years@ChrisMarisic quote from answer "you can emit such object". Sure you can.
-
JotaBe over 10 yearsThis is not an answer, but a comment.
-
secretAgentB about 10 years+1 But I'm not sure how is doing that going to help you.
-
theta-fish over 9 years@JotaBe, it actually is an answer, saying indirectly: "You can't do that." I believe it to be an incorrect answer, but an answer all the same.
-
Nils over 8 yearsI like the idea. However, there is no
ICollection.AddRange
. You'll have to either supply AddRange by use of extensions or try usingsource.ToList().ForEach(someObject.Add)
-
Geir Sagberg over 8 yearsWhoops, good catch! I'm so used to my extension methods I take them for granted. Will update answer.
-
Mojtaba Rezaeian about 8 yearsHow to do same thing in vb.net?
-
dalenewman over 5 yearsOn the matter of why you'd ever do this. When you pass in a model to a Razor template, it's easier to work with a dynamic object than a dictionary. So, if you have a dictionary you'd want to convert it to a dynamic object. Then, in your *.cshtml template, place-holders look like this: @Model.Name, instead of this: @Model["Name"].
-
Cruiser KID over 5 yearsIs there any implementation of AnonymousType in dotnet core
-
Abdul Rauf over 5 years@CruiserKID I don't know about any similar implementation. Although you can create and manipulate anonymous types in .NET Core just like you can do in .NET
-
bambams about 4 yearsSide note:
ExpandoObject
doesn't work with reflection because it's not a real object so if you happen to be passing the result to something that requires an object that will be reflected on that doesn't solve the problem. Your options appear to be fancy dynamic type creation (maybe benohead.com/blog/2013/12/26/…) or ... find/develop a new API that doesn't require an object that can be reflected on.