Existe-t-il un moyen rapide et élégant de mapper un objet à un dictionnaire et inversement?
IDictionary<string,object> a = new Dictionary<string,object>();
a["Id"]=1;
a["Name"]="Ahmad";
// .....
devient
SomeClass b = new SomeClass();
b.Id=1;
b.Name="Ahmad";
// ..........
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;
}
}
En utilisant des réflexions et des génériques dans deux méthodes d’extension, vous pouvez y parvenir.
D'accord, d'autres ont essentiellement utilisé la même solution, mais celle-ci utilise moins de réflexion, ce qui est plus performant et beaucoup plus lisible:
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();
}
}
Convertissez d'abord le dictionnaire en chaîne JSON avec Newtonsoft.
var json = JsonConvert.SerializeObject(advancedSettingsDictionary, Newtonsoft.Json.Formatting.Indented);
Puis désérialiser la chaîne JSON sur votre objet
var myobject = JsonConvert.DeserializeObject<AOCAdvancedSettings>(json);
Il semble que la réflexion ne soit qu'une aide ici. J'ai fait un petit exemple de conversion d'objet en dictionnaire et inversement:
[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;
}
Je recommande vivement le Castle DictionaryAdapter , l'un des secrets les mieux gardés de ce projet. Il vous suffit de définir une interface avec les propriétés souhaitées, et dans une ligne de code, l'adaptateur générera une implémentation, l'instanciera et synchronisera ses valeurs avec un dictionnaire que vous transmettez. Je l'utilise pour taper fortement mes AppSettings dans un projet web:
var appSettings =
new DictionaryAdapterFactory().GetAdapter<IAppSettings>(ConfigurationManager.AppSettings);
Notez que je n'ai pas eu besoin de créer une classe qui implémente IAppSettings - l'adaptateur le fait à la volée. De plus, bien que dans ce cas, je lis seulement, en théorie, si je définissais des valeurs de propriété sur appSettings, l'adaptateur maintiendrait le dictionnaire sous-jacent en synchronisation avec ces modifications.
La réflexion peut vous faire passer d’un objet à un dictionnaire en parcourant les propriétés.
Pour faire l'inverse, vous devrez utiliser un --- ExpandoObject (qui hérite déjà d'IDictionary, et l'a donc fait pour vous) en C #, à moins que vous ne puissiez en déduire le type de la collection d'entrées dans le dictionnaire en quelque sorte.
Donc, si vous êtes en mode .NET 4.0, utilisez un ExpandoObject, sinon vous avez beaucoup de travail à faire ...
Je pense que vous devriez utiliser la réflexion. Quelque chose comme ça:
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;
}
Il prend votre dictionnaire et boucle à travers et définit les valeurs. Vous devriez le rendre meilleur mais c'est un début. Vous devriez l'appeler comme ça:
SomeClass someClass = ConvertDictionaryTo<SomeClass>(a);
S'appuyant sur la réponse de Matías Fidemraizer, voici une version qui prend en charge la liaison à des propriétés d'objet autres que des chaînes.
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)
);
}
}
}
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);
}
}
Si vous utilisez Asp.Net MVC, examinez les éléments suivants:
public static RouteValueDictionary AnonymousObjectToHtmlAttributes(object htmlAttributes);
qui est une méthode publique statique sur la classe System.Web.Mvc.HtmlHelper.