Joe
Joe

Reputation: 6416

Build List<List<XElement>> from XML

I have an XML that is laid out to be reformatted into nested HTML table headers. I am working on getting each tier of the XML document into it's own list. For example:

<column name="Total" size="0">
  <column name="Users" size="0" />
</column>
<column name="Date" size="0" />
<column name="Unique" size="0">
  <column name="Clicks" size="0">
    <column name="RC" size="0" />
    <column name="CB" size="0" />
  </column>
</column>

From this example, columns "Total", "Date", and "Unique" should be in the first list. Columns "Users" and "Clicks" should be in the second list. And, columns "RC" and "CB" should be in the third list. This should be accomplished using recursion to make the method completely dynamic. Any help is greatly appreciated.

Upvotes: 1

Views: 1020

Answers (1)

Douglas
Douglas

Reputation: 54877

Here you go:

XElement root = XElement.Parse(@"
    <doc>
      <column1>
        <column2 />
      </column1>
      <column3 />
      <column4>
        <column5>
          <column6 />
          <column7 />
        </column5>
      </column4>
    </doc>");

List<List<XElement>> outerList = new List<List<XElement>>();
List<XElement> innerList = root.Elements().ToList();
while (innerList.Any())
{
    outerList.Add(innerList);
    innerList = innerList.SelectMany(element => element.Elements()).ToList();
}

Edit: If you want to strip ancestor XElement instances of their descendants within your list, then you could use the following:

XElement root = XElement.Parse(@"
    <table>
      <column name=""Total"" size=""0"">
        <column name=""Users"" size=""0"" />
      </column>
      <column name=""Date"" size=""0"" />
      <column name=""Unique"" size=""0"">
        <column name=""Clicks"" size=""0"">
          <column name=""RC"" size=""0"" />
          <column name=""CB"" size=""0"" />
        </column>
      </column>
    </table>");

List<List<XElement>> outerList = new List<List<XElement>>();
IEnumerable<XElement> innerList = root.Elements();
while (innerList.Any())
{
    outerList.Add(innerList.Select(e => new XElement(e.Name, e.Attributes())).ToList());
    innerList = innerList.SelectMany(element => element.Elements());
}

Note: For the record, your intuition that you should use recursion was correct. However, it is also well known that any recursive function can be converted to an iteration, typically by simulating the stack. Sometimes, this leads to bloated code; however, other times, the conversion lends itself naturally. In your case, if you were to recurse, your recursive parameter would have been the immediate children of the set of elements currently being considered – which already happens to be available in innerList, thus allowing us to use the innerList = innerList.<SequenceOperation> trick to substitute the recursion.

Upvotes: 1

Related Questions