递归地将XML导入对象
本文关键字:导入 对象 XML 递归 | 更新日期: 2023-09-27 18:04:31
我试图写一个方法,使用反射,以获得属性和设置它们的值,同时遍历XElement:
假设我有一个这样的类,它只提供要解析的XML值:
class XMLController
{
public string XML
{
get{
return @"<FieldGroup name='People' count='20'>
<Fields>
<Field Name='Jon' LastName='McFly'/>
<Field Name='Michael' LastName='Jackson'/>
</Fields>
</FieldGroup>";
}
}
}
我的对象是这样的:
class FieldGroup
{
public string Name {get;set;}
public string Count {get;set;}
public IEnumerable<Field> Fields {get;set;}
}
class Field
{
public string Name {get;set;}
public string LastName {get;set;}
}
映射器方法遍历XElement
,并且由于节点名称与对象名称匹配,我认为这有助于更多,但我还没有想出一些真正有用的东西。我不想传递类型,但是,该方法几乎可以处理以相同格式传入的所有XML。
它只知道XML节点和属性是匹配的名称。
这是我所做的,但没有真正起作用:
class XMLObjectMapper
{
public T Map<T>(XElement element) where T: class, new()
{
T entity = (T) Activator.CreateInstance(typeof(T));
if(element.HasAttributes)
{
MapXMLAttributesToObject<T>(element,entity);
}
if(element.HasElements)
{
foreach (var childElement in element.Elements())
{
//if the child element has child elements as well, we know this is a collection.
if(childElement.HasElements)
{
var property = GetProperty<T>(childElement.Name.LocalName);
property.SetValue(entity,new List<property.PropertyType>());
Map<T>(childElement);
}
else
{
var property = GetProperty<T>(childElement.Name.LocalName);
var type = Activator.CreateInstance(property.PropertyType);
type.Dump();
}
}
}
return entity;
}
private void MapXMLAttributesToObject<T>(XElement element, T entity)
{
foreach(XAttribute attribute in element.Attributes())
{
var property = GetProperty<T>(attribute.Name.LocalName);
property.SetValue(entity,attribute.Value);
}
}
private PropertyInfo GetProperty<T>(string propertyName)
{
return typeof(T).GetProperty(propertyName,BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
}
}
你在正确的轨道上,但是正如你所注意到的,你有一些错误。
下面这段代码不能编译,因为不能使用值(property.PropertyType
)代替类型名。c#是一种静态类型语言,所以类型必须在编译时就知道,而不是在变量中:
new List<property.PropertyType>()
但是,如果使用反射,则可以在运行时选择类型。我们可以这样做:
Activator.CreateInstance(typeof(List<>).MakeGenericType(collectionElementType))
另一个问题是你不能只调用Map<T>(childElement)
。首先,T不是正确的类型——它是父元素的类型,而不是子元素的类型。其次,子对象实际上是一个集合,Map<T>
不知道如何处理集合,只能处理单个对象。我们必须遍历子元素,映射到每个单个元素(使用集合中元素的类型调用Map<T>
——在您的示例中为Map<Field
),然后将它们全部添加到集合中。我已经做了一个新版本的Map<T>
的工作:
public T Map<T>(XElement element) where T : class, new()
{
T entity = (T)Activator.CreateInstance(typeof(T));
if (element.HasAttributes)
{
MapXMLAttributesToObject<T>(element, entity);
}
if (element.HasElements)
{
foreach (var childElement in element.Elements())
{
var property = GetProperty<T>(childElement.Name.LocalName);
// If the child element has child elements as well, we know this is a collection.
if (childElement.HasElements)
{
// Assume collections are of type IEnumerable<T> or List<T>
var collectionElementType = property.PropertyType.GetGenericArguments()[0];
// var collectionValue = new List<collectionElementType>()
var collectionValue = Activator.CreateInstance(typeof(List<>).MakeGenericType(collectionElementType));
foreach (var grandchildElement in childElement.Elements())
{
// var collectionElement = this.Map<collectionElementType>(grandchildElement);
var collectionElement = this.GetType().GetMethod("Map").MakeGenericMethod(collectionElementType).Invoke(this, new object[] { grandchildElement });
collectionValue.GetType().GetMethod("Add").Invoke(collectionValue, new object[] { collectionElement });
}
property.SetValue(entity, collectionValue, null);
}
else
{
// I'm not sure what this should do -- this case doesn't happen in your example.
throw new NotImplementedException();
}
}
}
return entity;
}
它当然需要一些更多的错误处理,我假设你想在我抛出NotImplementedException
的情况下做一些有用的事情。但是,它可以在您的示例中工作。