2013-03-19 3 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 соответствуют именам.

Это то, что я сделал, но на самом деле не работал:

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'? –

+0

На самом деле я не даю гибкости, к которой я нуждаюсь. – Tarik

ответ

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