¿Existe alguna forma elegante y rápida de asignar un objeto a un diccionario y viceversa?
Ejemplo:
IDictionary<string,object> a = new Dictionary<string,object>();
a["Id"]=1;
a["Name"]="Ahmad";
// .....
se convierte en
SomeClass b = new SomeClass();
b.Id=1;
b.Name="Ahmad";
// ..........
c#
.net
dictionary
reflection
mapping
Sawan
fuente
fuente
Respuestas:
Usando algo de reflexión y genéricos en dos métodos de extensión, puede lograrlo.
Correcto, otros hicieron casi la misma solución, pero esto usa menos reflexión, lo que es más en cuanto al rendimiento y mucho más legible:
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(); } }
fuente
GetType()
ya no se invocasomeObject
para cada propiedad en el primer método.Primero convierta el diccionario a una cadena JSON con Newtonsoft.
var json = JsonConvert.SerializeObject(advancedSettingsDictionary, Newtonsoft.Json.Formatting.Indented);
Luego deserialice la cadena JSON a su objeto
var myobject = JsonConvert.DeserializeObject<AOCAdvancedSettings>(json);
fuente
Parece que la reflexión solo ayuda aquí ... He hecho un pequeño ejemplo de conversión de objeto a diccionario y viceversa:
[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; }
fuente
Recomiendo encarecidamente Castle DictionaryAdapter , fácilmente uno de los secretos mejor guardados de ese proyecto. Solo necesita definir una interfaz con las propiedades que desea, y en una línea de código, el adaptador generará una implementación, la instanciará y sincronizará sus valores con un diccionario que ingrese. Lo uso para escribir fuertemente mis AppSettings en un proyecto web:
var appSettings = new DictionaryAdapterFactory().GetAdapter<IAppSettings>(ConfigurationManager.AppSettings);
Tenga en cuenta que no necesitaba crear una clase que implemente IAppSettings; el adaptador lo hace sobre la marcha. Además, aunque en este caso solo estoy leyendo, en teoría si estuviera configurando valores de propiedad en appSettings, el adaptador mantendría el diccionario subyacente sincronizado con esos cambios.
fuente
La reflexión puede llevarlo de un objeto a un diccionario iterando sobre las propiedades.
Para ir al revés, tendrá que usar un ExpandoObject dinámico (que, de hecho, ya hereda de IDictionary, y por lo tanto lo ha hecho por usted) en C #, a menos que pueda inferir el tipo de la colección de entradas en el diccionario de alguna manera.
Entonces, si está en la tierra de .NET 4.0, use un ExpandoObject, de lo contrario, tiene mucho trabajo por hacer ...
fuente
Creo que deberías usar la reflexión. Algo como esto:
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; }
Toma su diccionario, lo recorre y establece los valores. Deberías mejorarlo, pero es un comienzo. Deberías llamarlo así:
fuente
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; } }
fuente
Sobre la base de la respuesta de Matías Fidemraizer, aquí hay una versión que admite la vinculación a propiedades de objetos que no sean cadenas.
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) ); } } }
fuente
Si está utilizando Asp.Net MVC, eche un vistazo a:
public static RouteValueDictionary AnonymousObjectToHtmlAttributes(object htmlAttributes);
que es un método público estático en la clase System.Web.Mvc.HtmlHelper.
fuente
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); } }
fuente