Read the XML tree structure recursively in List <T> with child lists <T>
I have an XML like this:
And I have a Member class with a property name.
How can I read each Unit and its child Units into multiple generic List<Unit> that can have children of List<Unit> recursively using the latest .NET technology?
<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> This would do this using regular recursion:
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(); } } Why don't you implement a tree to store units. This would be much simpler and more natural than lists.
Take a look at this comment for a good implementation using LinkedList.
Cm.
- MSDN: Algorithms and Data Structures
- Tree data structure in C #
- Tree: Embedding a non-binary tree in C #
If you need to use List, you can use recursion to create it. I assume that your unit has a property (IList Unit.ChildUnits) for storing all child lists. If not, you might want to transfer Unit to another class that has this.
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; } The challenge is to write it as 1 LINQ query, but that is outside of me. LINQ is not easy / suitable for recursion.
I will draw a solution, I will not write it:
- read Xml in XDocument (or XmlDocument)
- define a
class Unit { ... ; ... List<Unit> Children; }class Unit { ... ; ... List<Unit> Children; } - if necessary, define the Units and Root classes. I will unravel this part here.
- Get a flat list of all Unit tags,
var units = doc.Descendants("Unit"); - Iterating over these elements, I assume that the parent node will always be before the nested Unit
- find the parent of each node in
var Lookup = new Dictionary<XNode, Unit> (); - If a parent element is found, add the current node (new element) to its daughters
- still add it to topList
- add a new element and XElement to the dictionary.
- A search dictionary is only needed when creating lists.
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(); } } You can use it as follows:
Unit.Convert(XDocument.Parse(xml).Root.Elements())