212

I am using 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 have written 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...??? } 
1

6 Answers 6

237

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); } 
Sign up to request clarification or add additional context in comments.

2 Comments

@bendewey I ask a similar question, would you please check it, here: stackoverflow.com/questions/13247449/…
It is like taking an aircraft carrier just to go fishing.
54

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

Comments

23

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

Comments

19

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

Comments

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

2 Comments

Doesn't this print all the level2's after all the level1's?
@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 :))
0

Asynchronous loading of the XML file can improve performance, especially if the file is large or if it takes a long time to load. In this example, we use the XDocument.LoadAsync method to load and parse the XML file asynchronously, which can help to prevent the application from becoming unresponsive while the file is being loaded.

Demo: https://dotnetfiddle.net/PGFE7c (using XML string parsing)

Implementation:

XDocument doc; // Open the XML file using File.OpenRead and pass the stream to // XDocument.LoadAsync to load and parse the XML asynchronously using (var stream = File.OpenRead("data.xml")) { doc = await XDocument.LoadAsync(stream, LoadOptions.None, CancellationToken.None); } // Select the level1 elements from the document and create an anonymous object for each element // with a Name property containing the value of the "name" attribute and a Children property // containing a collection of the names of the level2 elements var results = doc.Descendants("level1") .Select(level1 => new { Name = level1.Attribute("name").Value, Children = level1.Descendants("level2") .Select(level2 => level2.Attribute("name").Value) }); foreach (var result in results) { Console.WriteLine(result.Name); foreach (var child in result.Children) Console.WriteLine(" " + child); } 

Result:

A   A1   A2 B   B1   B2 C 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.