将类型对象动态地序列化为具体类型
本文关键字:类型 序列化 对象 动态 | 更新日期: 2023-09-27 18:18:16
你好,我正在解决下面的问题。
有两个参数
- 动态的对象(obj)(包含带值的属性)
- 一个字符串(typeName)(包含一个类型名,可以是任何与具体定义的类型范围相匹配的东西)。
请注意,obj是动态的,所以我在设计时不能访问它的任何类型或属性。
从这里,我需要将这个匿名对象转换为它的具体类型,用对象中包含的值初始化它并序列化。
我现在拥有的是:
public class ExampleObj
{
public int A { get; set; }
public string B { get; set; }
}
static void Main(string[] args)
{
var typeName = "ExampleObj";
object obj = new
{
A = 5,
B = "xx"
};
Type type = Type.GetType(typeName);
var serialiser = new XmlSerializer(type);
var sb = new StringBuilder();
serialiser.Serialize(new StringWriter(sb), obj);
}
最后一行失败的原因很明显(运行时现在不知道如何从类型对象转换为具体类型ExampleObj,这就是我卡住的地方。
我也考虑过Activator。创建实例,但我的知识和搜索并没有表明我如何在序列化之前用正确的值初始化这个实例。
请考虑下一个示例(对于typename,您需要提供包含该类型的名称空间):
var typeName = "YourNamespace.ExampleObj";
object obj = new
{
A = 5,
B = "xx"
};
var props = TypeDescriptor.GetProperties(obj);
Type type = Type.GetType(typeName);
ExampleObj instance = (ExampleObj)Activator.CreateInstance(type);
instance.A = (int)props["A"].GetValue(obj);
instance.B = (string)props["B"].GetValue(obj);
//serialize the instance now...
如果obj是var obj则:
var obj = new
{
A = 5,
B = "xx"
};
Type type = Type.GetType(typeName);
ExampleObj instance = (ExampleObj)Activator.CreateInstance(type);
instance.A = obj.A;
instance.B = obj.B;
这是我在控制器方法中的最终解决方案,该方法根据需要使用动态类型。我最初的问题的关键概念是,你不能(从我能收集到的东西中)自动进行角色转换。您需要某种切换逻辑,根据您想要支持的类型进行手动强制转换(或者使用第三方工具,如问题的评论部分所建议的)
[HttpPost]
public ActionResult SaveRule(int? id, ExpandoObject ruleObj, string ruleTypeName)
{
Type type = GetTypeFromName(ruleTypeName);
var instance = Activator.CreateInstance(type);
foreach (var prop in type.GetProperties())
{
object val;
((IDictionary<string, object>)ruleObj).TryGetValue(prop.Name, out val);
val = AutoMapObjectType(prop, val);
prop.SetValue(instance, val);
}
var serialiser = new XmlSerializer(type);
var sb = new StringBuilder();
serialiser.Serialize(new StringWriter(sb), instance);
if (id != null)
{
RuleDataAccess.SaveRule((int)id, sb.ToString());
}
return new JsonResult();
}
public static object AutoMapObjectType(PropertyInfo prop, object val)
{
if (prop.PropertyType.BaseType.Name == "Enum")
{
return Enum.Parse(prop.PropertyType, val.ToString());
}
else
{
switch (prop.PropertyType.Name)
{
case "Boolean":
return Convert.ToBoolean(short.Parse(val.ToString()));
case "String":
case "Int32":
case "Decimal":
return Convert.ChangeType(val, prop.PropertyType);
default:
return Convert.ChangeType(val, prop.PropertyType); //Attempt to convert with implicit casting/conversion, (will fail if explicit cases are not handled)
}
}
}