views:

7936

answers:

5

I got this XML file

<root>
 <level1 name="A">
  <level2 name="A1" />
  <level2 name="A2" />
 </level1>
 <level1 name="B">
  <level2 name="B1" />
  <level2 name="B2" />
 </level1>
 <level1 name="C" />
</root>

Could someone give me a C# code using LINQ, the simplest way to print this result:
(Note the extra space if it is a level2 node)

A
  A1
  A2
B
  B1
  B2
C

Currently I got this code

XDocument xdoc = XDocument.Load("data.xml"));
var lv1s = from lv1 in xdoc.Descendants("level1")
     select lv1.Attribute("name").Value;

foreach (var lv1 in lv1s)
{
 result.AppendLine(lv1);

 var lv2s = from lv2 in xdoc...???
}

Thank you!

+9  A: 

Try this.

XDocument xdoc = XDocument.Load("data.xml"));
var lv1s = from lv1 in xdoc.Descendants("level1")
           select new { 
               Header = lv1.Attribute("name").Value,
               Children = lv1.Descendants("level2")
           };


foreach (var lv1 in lv1s){
        result.AppendLine(lv1.Header);
        foreach(var lv2 in lv1.Children)
             result.AppendLine("     " + lv2.Attribute("name").Value);
}
bendewey
A: 
XDocument xdoc = XDocument.Load("data.xml");
var lv1s = xdoc.Root.Descendants("level1"); 
var lvs = lv1s.SelectMany(l=>
     new string[]{ l.Attribute("name").Value }
     .Union(
         l.Descendants("level2")
         .Select(l2=>"   " + l2.Attribute("name").Value)
      )
    );
foreach (var lv in lvs)
{
   result.AppendLine(lv);
}

Ps. You have to use .Root on any of these versions.

eglasius
Doesn't this print all the level2's after all the level1's?
sblom
@sblom oops, that's right, updated it with what I meant to post (ran a test against it, so I am sure it works now :))
eglasius
+2  A: 

A couple of plain old foreach loops provides a clean solution:

foreach (XElement level1Element in XElement.Load("data.xml").Elements("level1"))
{
    result.AppendLine(level1Element.Attribute("name").Value);

    foreach (XElement level2Element in level1Element.Elements("level2"))
    {
        result.AppendLine("  " + level2Element.Attribute("name").Value);
    }
}
dommer
+3  A: 

Or, if you want a more general approach - i.e. for nesting up to "levelN":

void Main()
{
    XElement rootElement = XElement.Load(@"c:\events\test.xml");

    Console.WriteLine(GetOutline(0, rootElement));  
}

private string GetOutline(int indentLevel, XElement element)
{
    StringBuilder result = new StringBuilder();

    if (element.Attribute("name") != null)
    {
        result = result.AppendLine(new string(' ', indentLevel * 2) + element.Attribute("name").Value);
    }

    foreach (XElement childElement in element.Elements())
    {
        result.Append(GetOutline(indentLevel + 1, childElement));
    }

    return result.ToString();
}
dommer
A: 

above way of of reading XML data is not suitable special when xml file is very large, so loading a very large file in memory is very fake approach, so you can visit the following link to read data from a very large xml file

http://msdn.microsoft.com/en-us/library/bb387013.aspx

Arshad