将类型对象动态地序列化为具体类型

时间:2014-08-17 15:23:46

标签: c# serialization dynamic reflection types

您好我很难解决以下问题。

我提供了两个参数

  • 对象(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.CreateInstance可以动态创建一个实例,但我的知识和搜索并没有说明在序列化之前如何用正确的值初始化这个实例。

2 个答案:

答案 0 :(得分:0)

请考虑下一个示例(对于提供包含类型的命名空间所需的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;

答案 1 :(得分:0)

这是我在控制器方法中的最终解决方案,它使用动态类型按要求工作。我原来问题的关键概念是你不能(形成我能收集的东西)自动进行演员表演。您需要某种切换逻辑,根据您要支持的类型进行手动投射(或者使用问题评论部分中建议的第三方工具)

    [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)
            }
        }
    }