递归映射ExpandoObject [英] Recursively Mapping ExpandoObject
问题描述
在我的应用我有,以创建/删除的运行时性能使用ExpandoObject;不过,我有一个函数返回的ExpandoObject映射到相应的对象/类。所以我必须想出了一个小的映射,没有工作,但有三个问题:
- 它不会递归的内部对象映射ExpandoObject $像预想的那样b $湾
- 当我尝试映射INT为可空只是它会抛出一个类型
不匹配,因为我无法找到一个方法来检测和。正常投它 - 字段不能映射
公共字符串属性;
代码:
的 I-执行:的
公共静态类映射< T>其中T:类
{
#区域属性
私人静态只读字典<字符串的PropertyInfo> PropertyMap;
#endregion
#地区的构造函数
静态映射(){PropertyMap = typeof运算(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic可| BindingFlags.Instance).ToDictionary(p值=> p.Name.ToLower()中,p =指p); }
#endregion
#地区的方法
公共静态无效的地图(ExpandoObject源,T目的地)
{
如果(来源== NULL)
抛出新的ArgumentNullException(源);
如果(目标== NULL)
抛出新的ArgumentNullException(目标);
的foreach(源变种KV)
{
的PropertyInfo磷;
如果(PropertyMap.TryGetValue(kv.Key.ToLower(),出P))
{
型propType = p.PropertyType;
如果(kv.Value == NULL)如果
{
(propType.IsByRef&安培;!&安培;!propType.Name =Nullable`1)
{
抛出新的ArgumentException(不能为空);
}
}
,否则如果(kv.Value.GetType()= propType!)
{
抛出新的ArgumentException(类型不匹配);
}
p.SetValue(目的地,kv.Value,NULL);
}
}
}
#endregion
}
的二:使用方法:的
公共静态无效的主要()
{
类C =新的Class();
动态O =新ExpandoObject();
o.Name =卡尔;
o.Level = 7;
o.Inner =新将InnerClass
{
NAME =卡尔内,
级= 10
};
映射器LT;类> .MAP(O,C);
Console.Read();
}
内部类类
{
公共字符串名称{;组; }
公众诠释?等级{搞定;组; }
公共内蒙古将InnerClass {搞定;组; }
公共字符串属性;
}
内部类将InnerClass
{
公共字符串名称{;组; }
公众诠释?等级{搞定;组; }
}
3如果属性格式化这样
公共字符串属性;
的获取属性没有得到它。
块引用>
哦,那不是一个性质,这是一个领域。如果你想考虑的领域也是如此。
静态映射()
{
PropertyMap = typeof运算(T ).GetProperties(BindingFlags.Public |
BindingFlags.NonPublic可|
BindingFlags.Instance)
.ToDictionary(p值=> p.Name.ToLower()中,p =指p);
FieldMap = typeof运算(T).GetFields(BindingFlags.Public |
BindingFlags.NonPublic可|
BindingFlags.Instance)
.ToDictionary(F => F。 Name.ToLower()中,f = F)的温度;
}
2 - 当我尝试映射到INT可空只是它会抛出一个类型不匹配,因为我无法找到一种方法来检测并正确投放。
块引用>
为什么检查
可空
键入,让反射看着办吧。如果值是有效的,它会被分配。公共静态无效的地图(ExpandoObject源,T目的地)
{
如果(来源== NULL)
抛出新的ArgumentNullException(源);
如果(目标== NULL)
抛出新的ArgumentNullException(目标);
的foreach(源变种KV)
{
的PropertyInfo磷;
如果(PropertyMap.TryGetValue(kv.Key.ToLower(),出P))
{
p.SetValue(目的地,kv.Value,NULL);
}
,否则
{
字段信息F;
如果(FieldMap.TryGetValue(kv.Key.ToLower(),从F))
{
f.SetValue(目的地,kv.Value);
}
}
}
}
1 - 它不会递归ExpandoObject的内部对象映射像预想的那样
块引用>
似乎是有效的工作,你的
将InnerClass
至少C类=新的Class()。
动态O =新ExpandoObject();
o.Name =卡尔;
o.Level = 7;
o.Inner =新将InnerClass
{
NAME =卡尔内,
级= 10
};
o.Property =我的财产价值; //不要忘记设置这个
映射器LT;类> .MAP(O,C);
修改:根据您的意见,我已经创建了两个重载方法
MergeProperty
。你可以写类似的重载方法领域。公共静态无效MergeProperty(的PropertyInfo PI,ExpandoObject源,对象目标)
{
型propType = pi.PropertyType;
//不递归值类型,可空< T>和字符串
如果(propType.IsValueType || propType == typeof运算(字符串))
{
VAR sourceVal = source.First(KVP => kvp.Key == pi.Name) 。值;
如果(sourceVal!= NULL)
pi.SetValue(目标,sourceVal,NULL);
}
,否则//递归映射内部类属性
{
VAR道具= propType.GetProperties(BindingFlags.Public |
BindingFlags.NonPublic可|
的BindingFlags .Instance);
的foreach(以道具变种P)
{
VAR sourcePropValue = source.First(KVP => kvp.Key == pi.Name).value的;
VAR targetPropValue = pi.GetValue(目标,NULL);
如果(sourcePropValue!= NULL)
{
如果(targetPropValue == NULL)//替换
{
pi.SetValue(目标,源。首先(KVP => kvp.Key == pi.Name).value的,NULL);
}
,否则
{
MergeProperty(P,sourcePropValue,targetPropValue);
}
}
}
}
}
公共静态无效MergeProperty(的PropertyInfo PI,对象源,对象目标)
{
型propType = pi.PropertyType;
的PropertyInfo sourcePi = source.GetType()的getProperty(pi.Name)。
//不递归值类型,可空< T>和字符串
如果(propType.IsValueType || propType == typeof运算(字符串))
{
VAR sourceVal = sourcePi.GetValue(源,NULL);
如果(sourceVal!= NULL)
pi.SetValue(目标,sourceVal,NULL);
}
,否则//递归映射内部类属性
{
VAR道具= propType.GetProperties(BindingFlags.Public |
BindingFlags.NonPublic可|
的BindingFlags .Instance);
的foreach(以道具变种P)
{
VAR sourcePropValue = sourcePi.GetValue(源,NULL);
VAR targetPropValue = pi.GetValue(目标,NULL);
如果(sourcePropValue!= NULL)
{
如果(targetPropValue == NULL)//替换
{
pi.SetValue(目标,sourcePi .GetValue(源,NULL),NULL);
}
,否则
{
MergeProperty(P,sourcePropValue,targetPropValue);
}
}
}
}
}
您可以使用的方法是这样的:
公共静态无效的地图(ExpandoObject源,T目的地)
{
如果(来源== NULL)
抛出新的ArgumentNullException(源);
如果(目标== NULL)
抛出新的ArgumentNullException(目标);
的foreach(源变种KV)
{
的PropertyInfo磷;
如果(PropertyMap.TryGetValue(kv.Key.ToLower(),出P))
{
MergeProperty(P,来源,目的地);
}
,否则
{
//做类似的字段合并
}
}
}
In my application i have to use ExpandoObject in order to create/delete properties during the runtime; However, i have to map the returned ExpandoObject of a function to the corresponding object/class. So i have came up with a small Mapper that does the job but with 3 problems:
- It does not recursively map the inner objects of the ExpandoObject as supposed.
- When i try to map int to a Nullable simply it will throw a type mismatch because i can't find a way to detect and cast it properly.
- Fields can't be mapped
public string Property;
.Code:
I- Implementation:
public static class Mapper<T> where T : class { #region Properties private static readonly Dictionary<string, PropertyInfo> PropertyMap; #endregion #region Ctor static Mapper() { PropertyMap = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).ToDictionary(p => p.Name.ToLower(), p => p); } #endregion #region Methods public static void Map(ExpandoObject source, T destination) { if (source == null) throw new ArgumentNullException("source"); if (destination == null) throw new ArgumentNullException("destination"); foreach (var kv in source) { PropertyInfo p; if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p)) { Type propType = p.PropertyType; if (kv.Value == null) { if (!propType.IsByRef && propType.Name != "Nullable`1") { throw new ArgumentException("not nullable"); } } else if (kv.Value.GetType() != propType) { throw new ArgumentException("type mismatch"); } p.SetValue(destination, kv.Value, null); } } } #endregion }
II: Usage:
public static void Main() { Class c = new Class(); dynamic o = new ExpandoObject(); o.Name = "Carl"; o.Level = 7; o.Inner = new InnerClass { Name = "Inner Carl", Level = 10 }; Mapper<Class>.Map(o, c); Console.Read(); } internal class Class { public string Name { get; set; } public int? Level { get; set; } public InnerClass Inner { get; set; } public string Property; } internal class InnerClass { public string Name { get; set; } public int? Level { get; set; } }
解决方案3- If the property is formated like this
public string Property;
the get properties does not get it.Oh, that's not a property, that's a field. If you want consider fields as well.
static Mapper() { PropertyMap = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .ToDictionary(p => p.Name.ToLower(), p => p); FieldMap = typeof(T).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) .ToDictionary(f => f.Name.ToLower(), f => f); }
2- When i try to map int to a Nullable simply it will throw a type mismatch because i can't find a way to detect and cast it properly.
Why check for
Nullable
type, let reflection figure it out. If value is valid, it will be assigned.public static void Map(ExpandoObject source, T destination) { if (source == null) throw new ArgumentNullException("source"); if (destination == null) throw new ArgumentNullException("destination"); foreach (var kv in source) { PropertyInfo p; if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p)) { p.SetValue(destination, kv.Value, null); } else { FieldInfo f; if (FieldMap.TryGetValue(kv.Key.ToLower(), out f)) { f.SetValue(destination, kv.Value); } } } }
1 - It does not recursively map the inner objects of the ExpandoObject as supposed.
Seems to work for your
InnerClass
at least.Class c = new Class(); dynamic o = new ExpandoObject(); o.Name = "Carl"; o.Level = 7; o.Inner = new InnerClass { Name = "Inner Carl", Level = 10 }; o.Property = "my Property value"; // dont forget to set this Mapper<Class>.Map(o, c);
EDIT: based on your comments, I've create two overloaded methods
MergeProperty
. You can write similarly overloaded methods for fields.public static void MergeProperty(PropertyInfo pi, ExpandoObject source, object target) { Type propType = pi.PropertyType; // dont recurse for value type, Nullable<T> and strings if (propType.IsValueType || propType == typeof(string)) { var sourceVal = source.First(kvp => kvp.Key == pi.Name).Value; if(sourceVal != null) pi.SetValue(target, sourceVal, null); } else // recursively map inner class properties { var props = propType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (var p in props) { var sourcePropValue = source.First(kvp => kvp.Key == pi.Name).Value; var targetPropValue = pi.GetValue(target, null); if (sourcePropValue != null) { if (targetPropValue == null) // replace { pi.SetValue(target, source.First(kvp => kvp.Key == pi.Name).Value, null); } else { MergeProperty(p, sourcePropValue, targetPropValue); } } } } } public static void MergeProperty(PropertyInfo pi, object source, object target) { Type propType = pi.PropertyType; PropertyInfo sourcePi = source.GetType().GetProperty(pi.Name); // dont recurse for value type, Nullable<T> and strings if (propType.IsValueType || propType == typeof(string)) { var sourceVal = sourcePi.GetValue(source, null); if(sourceVal != null) pi.SetValue(target, sourceVal, null); } else // recursively map inner class properties { var props = propType.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); foreach (var p in props) { var sourcePropValue = sourcePi.GetValue(source, null); var targetPropValue = pi.GetValue(target, null); if (sourcePropValue != null) { if (targetPropValue == null) // replace { pi.SetValue(target, sourcePi.GetValue(source, null), null); } else { MergeProperty(p, sourcePropValue, targetPropValue); } } } } }
You can use the methods this way:
public static void Map(ExpandoObject source, T destination) { if (source == null) throw new ArgumentNullException("source"); if (destination == null) throw new ArgumentNullException("destination"); foreach (var kv in source) { PropertyInfo p; if (PropertyMap.TryGetValue(kv.Key.ToLower(), out p)) { MergeProperty(p, source, destination); } else { // do similar merge for fields } } }
这篇关于递归映射ExpandoObject的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!