【问题标题】:Importing XML to Objects Recursively以递归方式将 XML 导入对象
【发布时间】:2013-03-19 13:58:07
【问题描述】:

我正在尝试编写一个使用反射的方法,以便在遍历 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);
    }
}

【问题讨论】:

  • XmlSerializer 有问题吗?
  • 不幸的是,并没有真正提供我需要的灵活性。

标签: c# asp.net xml


【解决方案1】:

您在正确的轨道上,但正如您所注意到的,您遇到了一些错误。

以下代码无法编译,因为您不能使用值 (property.PropertyType) 代替类型名称。 C# 是一种静态类型语言,因此必须在编译时知道类型,而不是在变量中:

new List<property.PropertyType>()

但是,如果使用反射,则可以在运行时选择类型。我们可以这样做:

Activator.CreateInstance(typeof(List<>).MakeGenericType(collectionElementType))

你遇到的另一个问题是你不能只打电话给Map&lt;T&gt;(childElement)。首先,T 不是正确的类型——它是父元素的类型,而不是子元素的类型。其次,child其实是一个集合,Map&lt;T&gt;不知道怎么处理集合,只知道个别对象。我们必须遍历子元素,映射到每个单独的元素(使用集合中元素的类型调用Map&lt;T&gt; - 在您的示例中为Map&lt;Field),然后将它们全部添加到集合中。我制作了一个新版本的 Map&lt;T&gt;,它可以工作:

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 的情况下做一些有用的事情。但是,它适用于您的示例。

【讨论】:

  • 你救了我的命。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-23
  • 1970-01-01
  • 1970-01-01
  • 2015-03-20
相关资源
最近更新 更多