2013-03-19 86 views
0

我試圖寫它使用反射,以獲取屬性並將其值設置在遍歷的XElement的方法:導入XML到對象遞歸

可以說我有這樣一類僅提供我的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和由於節點南es與匹配的對象的名稱我認爲這有助於多一點,但我還沒有拿出一些真正有用的東西。我不想傳遞這個類型,但是,該方法幾乎可以處理傳入的所有XML格式相同的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); 
    } 
} 
+0

「XmlSerializer」有問題嗎? – 2013-03-19 04:06:03

+0

不幸的是,我不需要靈活性。 – Tarik 2013-03-19 04:17:55

回答

2

你在正確的軌道上,但你已經注意到了,你有一些錯誤。

由於無法使用值(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的情況下非常有用。但是,它適用於您的示例。

+0

你救了我的命。謝謝! – Tarik 2013-03-19 16:06:49