2011-05-28 5 views
8

tengo un XML como esto:una estructura de árbol XML de forma recursiva en una lista <T> con niños enumera <T>

Y tengo una clase miembro con propiedad Name.

¿Cómo puedo leer cada unidad y sus unidades secundarias en múltiples genéricos List<Unit> que pueden tener nuevamente hijos List<Unit> de forma recursiva con la última tecnología .NET?

<Root> 
    <Units Name="Test1"> 
    <Unit Name="Test11" /> 
    <Unit Name="Test12"> 
     <Unit Name="Test21" /> 
     <Unit Name="Test22" /> 
     <Unit Name="Test23"> 
     <Unit Name="Test31" /> 
     <Unit Name="Test32" /> 
     <Unit Name="Test33" /> 
     </Unit> 
     <Unit Name="Test24" /> 
    </Unit> 
    </Units> 
    <Units Name="Test2" /> 
    <!-- ... --> 
    <Units Name="Test3" /> 
    <!-- ... --> 
    <Units Name="Test4" /> 
</Root> 
+0

habría pensado que esto era tan sencillo una implementación de la recursividad como podría obtener ... – cristobalito

+0

@christoba, pero el código para atravesar un XDoc/XmlDoc es algo difícil de manejar. Es posible sin embargo. Sugiero reemplazarlo con un diccionario en mi respuesta. –

Respuesta

13

Esto lo haría, utilizando la recursividad claro:

public class Unit 
{ 
    public string Name { get; set; } 
    public List<Unit> Children { get; set; } 
} 

class Program 
{ 
    public static void Main() 
    { 
     XDocument doc = XDocument.Load("test.xml"); 
     List<Unit> units = LoadUnits(doc.Descendants("Units").Elements("Unit")); 
    } 

    public static List<Unit> LoadUnits(IEnumerable<XElement> units) 
    { 
     return units.Select(x=> new Unit() 
           { Name = x.Attribute("Name").Value, 
            Children = LoadUnits(x.Elements("Unit")) 
           }).ToList(); 
    } 
} 
+0

@BrokenGlass ¿Cómo puedo obtener TopLevelUnits de este hierarchicalData? – Vishal

1

¿Por qué no implementar un árbol para almacenar unidades. Eso sería mucho más fácil y natural que las Listas.

Eche un vistazo a this comment para una buena implementación usando LinkedList.

Ver

Si TIENE utilizar la lista, entonces puede usar la recursividad para construirlo. Supongo que su Unidad tiene una propiedad (IList Unit.ChildUnits) para contener a todos los niños List. Si no, puede envolver a Unit en otra clase que tenga esto.

public List<Unit> LoadAllUnits(XMLNode rootNode){ 
    List<Unit> allUnits = new List<Unit>(); 
    foreach(var childNode in rootNode.ChildNodes){ 
     allUnits.Add(LoadAllSubUnits(childNode); 
    } 
    return allUnits; 
} 


private Unit LoadAllSubUnits(XMLNode node){ 
    Unit u = GetUnitFromCurrentNode(node); // Converts current node into Unit object 
    if(root.HasChildNode){ 
     foreach(var childNode in node.ChildNodes){ 
      u.ChildUnits.Add(LoadAllSubUnits(childNode); 
     } 
    } 
    return u; 
} 
+0

- Tengo que usar la lista Y ... la lista con todos sus elementos secundarios La lista debe leerse como datos jerárquicos en una base de datos sql relacional. – msfanboy

+0

en ese caso usa recursión para poner todos los nodos secundarios en una lista. Actualizaré la respuesta. – YetAnotherUser

1

El desafío sería escribirlo como 1 consulta LINQ, pero eso me supera. LINQ no es fácil/adecuado para la recursión.

Voy a esbozar una solución, no voy a escribirlo:

  • leer el XML en un XDocument (o XmlDocument)
  • definen un class Unit { ... ; ... List<Unit> Children; }
  • definir las unidades y la raíz clases si lo desea. Voy a aplanar la parte aquí
  • obtener una lista plana de todas las etiquetas de unidad, var units = doc.Descendants("Unit");
  • iterar sobre los elementos, supongo que un nodo padre siempre vendrá antes de que un anidado Unidad
  • buscar el padre de cada nodo en una var Lookup = new Dictionary<XNode, Unit>();
  • si se descubre que un padre, añadir el nodo actual (nueva Unidad) a sus niños
  • demás agregar a una primera lista
  • añadir la nueva unidad y la XElement al diccionario.
  • el diccionario de búsqueda solo es necesario al crear las listas.
1
class Unit 
{ 
    public string Name; 
    public List<Unit> Children; 

    public Unit(string name, List<Unit> children) 
    { 
     Name = name; 
     Children = children; 
    } 

    public static Unit Convert(XElement elem) 
    { 
     return new Unit(elem.Attribute("Name").Value, Convert(elem.Elements())); 
    } 

    public static List<Unit> Convert(IEnumerable<XElement> elems) 
    { 
     return elems.Select(Unit.Convert).ToList(); 
    } 
} 

Se puede utilizar la siguiente manera:

Unit.Convert(XDocument.Parse(xml).Root.Elements()) 
+0

Elegante, pero me gustaría mantener esos métodos Convert fuera de la clase. –

Cuestiones relacionadas