类型转换——在c#运行时动态地将一个类映射到另一个类
本文关键字:一个 映射 另一个 运行时 动态 类型转换 | 更新日期: 2023-09-27 17:50:59
我正在尝试构建一个系统,将数据从垂直键值对存储系统转换为更传统的水平列存储。
源数据看起来像这样
public class Container
{
public string Type { get; set; }
public IEnumerable<Attribute> Attributes { get; set; }
public IEnumerable<Container> RelatedContainers { get; set; }
}
public class Attributes
{
public string Name{ get; set; }
public string Value { get; set; }
}
它将生成类似
的数据public class Person
{
public string Name { get; set; }
public IEnumerable<Address> Addresses { get; set; }
}
public class Address
{
public string Line1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
在这种情况下有一些陷阱。首先,直到运行时我才知道目标类型中的所有字段。我有一个粗略的解决方案,可以在运行时根据源数据的结构生成新的类。
我想不出一个好方法来映射数据本身到新的类。我希望有人能指出一个更简单的方法来解决这个问题,或者在我正在走的道路上的下一步有一些帮助。
这里有一些代码,我认为可以为您提供一些开始的东西。它不处理嵌套对象,但这里应该有足够的内容供您填补空白。
它使用问题中的类,并填充一个Address对象。方法"CreateObjectFromContainer"是实际执行工作的地方。
using System;
using System.Collections.Generic;
using System.Linq;
namespace PopulateFromAttributes
{
class Program
{
static void Main(string[] args)
{
// Set up some test data - an address in a Container
var attributeData = new List<Attributes>
{
new Attributes { Name = "Line1", Value = "123 Something Avenue" },
new Attributes { Name = "City", Value = "Newville" },
new Attributes { Name = "State", Value = "New York" },
new Attributes { Name = "Zip", Value = "12345" },
};
Container container = new Container { Type = "Address", Attributes = attributeData };
// Instantiate and Populate the object
object populatedObject = CreateObjectFromContainer("PopulateFromAttributes", container);
Address address = populatedObject as Address;
// Output values
Console.WriteLine(address.Line1);
Console.WriteLine(address.City);
Console.WriteLine(address.State);
Console.WriteLine(address.Zip);
Console.ReadKey();
}
/// <summary>
/// Creates the object from container.
/// </summary>
/// <param name="objectNamespace">The namespace of the Type of the new object.</param>
/// <param name="container">The container containing the object's data.</param>
/// <returns>Returns a newly instantiated populated object.</returns>
private static object CreateObjectFromContainer(string objectNamespace, Container container)
{
// Get the Type that we need to populate and instantiate an object of that type
Type newType = Type.GetType(string.Format("{0}.{1}", objectNamespace, container.Type));
object newObject = Activator.CreateInstance(newType);
// Pass each attribute and populate the values
var properties = newType.GetProperties();
foreach (var property in properties)
{
var singleAttribute = container.Attributes.Where(a => a.Name == property.Name).FirstOrDefault();
if (singleAttribute != null)
{
property.SetValue(newObject, singleAttribute.Value, null);
}
}
return newObject;
}
}
public class Container
{
public string Type { get; set; }
public IEnumerable<Attributes> Attributes { get; set; }
public IEnumerable<Container> RelatedContainers { get; set; }
}
public class Attributes
{
public string Name { get; set; }
public string Value { get; set; }
}
public class Address
{
public string Line1 { get; set; }
public string City { get; set; }
public string State { get; set; }
public string Zip { get; set; }
}
}
使用。net反射来绑定目标类怎么样?我找到了一个例子,我相信它会给你提供你想要的方法:
http://www.codeproject.com/Articles/55710/Reflection-in-NET我可以提供的一条建议是使用System.Convert.ChangeType(…)方法在可能的情况下将值强制转换为目标类型,如果您从字符串值开始(如上面的代码所示),则在目标类型上查找静态Parse(…)方法。
这看起来是可行的:
object CreateObjectFromNVPair(Container c)
{
Type t = Type.GetType(this.GetType().Namespace + "." + c.Type);
object o = Activator.CreateInstance(t);
if (c.Attributes != null)
{
foreach (Attribute a in c.Attributes)
{
PropertyInfo pi = o.GetType().GetProperty(a.Name);
pi.SetValue(o, a.Value, null);
}
}
if (c.RelatedContainers != null)
{
foreach (Container c2 in c.RelatedContainers)
{
Type lt = typeof(List<>);
Type t2 = Type.GetType(this.GetType().Namespace + "." + c2.Type);
PropertyInfo pi = o.GetType().GetProperty(c2.Type + "List");
object l = pi.GetValue(o, null);
if (l == null)
{
l = Activator.CreateInstance(lt.MakeGenericType(new Type[] { t2 }));
pi.SetValue(o, l, null);
}
object o2 = CreateObjectFromNVPair(c2);
MethodInfo mi = l.GetType().GetMethod("Add");
mi.Invoke(l, new object[] { o2 });
}
}
return o;
}
可能需要对命名空间和用于CreateInstance的Activator或Assembly进行一些更改。
注意:我从复数列表重命名为在末尾添加"List"以保持一致性。