Create a list of attribute of nested nodes from XML file

I have an XML file as following:

<?xml version="1.0" encoding="utf-8"?>
<files>
    <file name="1">
        <file name="4">     
        </file> 
    </file>
    <file name="2">
    </file>
    <file name="3">
        <file name="5">
            <file name="7">
            </file>
        </file>
    </file>
</files>

Now I want to create a list of strings/numbers which save all the attribute name in a list/array which has the nested nodes hierarchy in it. For example for the above XML file the expected list would be,

(1,4
2
3,5,7)

Because I could could know the desired node in which level is.

Would you please let me know what is your idea about having such a list?

Update: After Jon's answer, If the children nodes are in the same hierarchy, then would in the following way.

The XML file:

<files>
<file name="1">
    <file name="4"/>     
    <file name="2">
      <file name="3"/>
    </file> 
</file>
<file name="5"/>

and the desired output:

1,4
1,2
1,2,3
5

PS. After testing for some examples I noticed that I have to have first parent and then children in case when there are two or more children from the same level.

Jon Skeet
people
quotationmark

It seems that you possibly want:

  • For all file elements which don't have a child file element
  • Work out the file element ancestry
  • Take the name attribute for each ancestor, and join it together

So I think this should work, creating an IEnumerable<List<int>>:

var hierarchy = doc.Descendants("file")
                   .Where(x => x.Element("file") == null)
                   .Select(x => x.AncestorsAndSelf("file")
                                 .Reverse()
                                 .Select(f => (int) f.Attribute("name"))
                                 .ToList());

Complete example:

using System;
using System.Linq;
using System.Xml.Linq;

class Test
{
    static void Main()
    {
        var doc = XDocument.Load("test.xml");
        var hierarchy = doc.Descendants("file")
                   .Where(x => x.Element("file") == null)
                   .Select(x => x.AncestorsAndSelf("file")
                                 .Reverse()
                                 .Select(f => (int) f.Attribute("name"))
                                 .ToList());

        foreach (var item in hierarchy)
        {
            Console.WriteLine(string.Join(", ", item));
        }
    }
}

Output:

1, 4
2
3, 5, 7

An alternative approach is to take all top-level file elements, and for each one, find all the descendants:

using System;
using System.Linq;
using System.Xml.Linq;

class Test
{
    static void Main()
    {
        var doc = XDocument.Load("test.xml");
        var hierarchy = doc.Root.Elements("file")
                   .Select(x => x.DescendantsAndSelf("file")
                                 .Select(f => (int) f.Attribute("name"))
                                 .ToList());

        foreach (var item in hierarchy)
        {
            Console.WriteLine(string.Join(", ", item));
        }
    }
}

Now both of those give the same result at the moment, because you've basically got a "line" from each top-level file element. However, you should consider what you want the result to be if you had XML like this:

<files>
    <file name="1">
        <file name="4"/>     
        <file name="2">
          <file name="3"/>
        </file> 
    </file>
    <file name="5"/>
</files>

Here the top-level element with name 1 has two children - what would you want the result to be?

people

See more on this question at Stackoverflow