LINQ to read XML

C#XmlLinqLinq to-Xml

C# Problem Overview


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...???
}

C# Solutions


Solution 1 - C#

Try this.

using System.Xml.Linq;

void Main()
{
    StringBuilder result = new StringBuilder();

    //Load xml
    XDocument xdoc = XDocument.Load("data.xml");

    //Run query
    var lv1s = from lv1 in xdoc.Descendants("level1")
               select new { 
                   Header = lv1.Attribute("name").Value,
                   Children = lv1.Descendants("level2")
               };
    
    //Loop through results
    foreach (var lv1 in lv1s){
            result.AppendLine(lv1.Header);
            foreach(var lv2 in lv1.Children)
                 result.AppendLine("     " + lv2.Attribute("name").Value);
    }
	
    Console.WriteLine(result);
}

Solution 2 - C#

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();
}

Solution 3 - C#

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);
    }
}

Solution 4 - C#

Here are a couple of complete working examples that build on the @bendewey & @dommer examples. I needed to tweak each one a bit to get it to work, but in case another LINQ noob is looking for working examples, here you go:

//bendewey's example using data.xml from OP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

class loadXMLToLINQ1
{
	static void Main( )
	{
		//Load xml
		XDocument xdoc = XDocument.Load(@"c:\\data.xml"); //you'll have to edit your path

		//Run query
		var lv1s = from lv1 in xdoc.Descendants("level1")
		   select new 
		   { 
			   Header = lv1.Attribute("name").Value,
			   Children = lv1.Descendants("level2")
			};

		StringBuilder result = new StringBuilder(); //had to add this to make the result work
		//Loop through results
		foreach (var lv1 in lv1s)
		{
			result.AppendLine("  " + lv1.Header);
			foreach(var lv2 in lv1.Children)
			result.AppendLine("    " + lv2.Attribute("name").Value);
		}
		Console.WriteLine(result.ToString()); //added this so you could see the output on the console
	}
}

And next:

//Dommer's example, using data.xml from OP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

class loadXMLToLINQ
{
static void Main( )
	{
		XElement rootElement = XElement.Load(@"c:\\data.xml"); //you'll have to edit your path
		Console.WriteLine(GetOutline(0, rootElement));  
	}

static 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();
	}
}

These both compile & work in VS2010 using csc.exe version 4.0.30319.1 and give the exact same output. Hopefully these help someone else who's looking for working examples of code.

EDIT: added @eglasius' example as well since it became useful to me:

//@eglasius example, still using data.xml from OP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

class loadXMLToLINQ2
{
	static void Main( )
	{
		StringBuilder result = new StringBuilder(); //needed for result below
		XDocument xdoc = XDocument.Load(@"c:\\deg\\data.xml"); //you'll have to edit your path
		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);
		}
		Console.WriteLine(result);//added this so you could see the result
	}
}

Solution 5 - C#

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAximiliView Question on Stackoverflow
Solution 1 - C#bendeweyView Answer on Stackoverflow
Solution 2 - C#dommerView Answer on Stackoverflow
Solution 3 - C#dommerView Answer on Stackoverflow
Solution 4 - C#delliottgView Answer on Stackoverflow
Solution 5 - C#eglasiusView Answer on Stackoverflow