Descendants vs. Elements in Linq to XML

Linq to XML is a nice and productive framework on top of XmlReader. When you want to find specific elements you have two options – Elements and Descendants – but there is an important distinction between these two.

The methods are members of XContainer class: that means any XDocument and XElement behaves the same. What does the documentation say?

Starting with Elements(XName).

Returns a filtered collection of the child elements of this element or document, in document order. Only elements that have a matching XName are included in the collection.

And secondly the Descendants(XName) method.

Returns a filtered collection of the descendant elements for this document or element, in document order. Only elements that have a matching XName are included in the collection.

They look almost the same, but the key difference is in the child vs. descendant elements part. Let’s examine the behaviour with a short example.

We create a simple XML file with a few fields. Note that a field element can appear under the weights as well as the ratio.

<?xml version='1.0' encoding='utf-8' ?>
<weights>
    <field>Field #1</field>
    <ratio>
        <field>Field #2</field>
    </ratio>
    <field>Field #3</field>
</weights>

After that, we make a program that loads the XML file, scans for fields of the root element and prints the output to a console window—starting with the Elements method.

var document = XDocument.Parse(sampleXml);
var root = document.Root;

var elements = root.Elements("field");
foreach (var item in elements) 
{
    Console.WriteLine(item.Value);
}

The output looks like this:

Field #1
Field #3

This method finds only the direct children of the queried element—in this case the weights. On the other hand, the Descendants method works slightly different.

var descendants = root.Descendants("field");
foreach (var item in descendants) 
{
    Console.WriteLine(item.Value);
}

The output of this code is:

Field #1
Field #2
Field #3

You can see that any inner field element is added to the collection, so this method finds all the direct and indirect descendants.

I have found that this subtle difference is often ignored and for flat XML files you might even overlook it. However, be aware of it and use the appropriate method for what you are planning to do.

Comments

Marcin Wasko: Initial testing shows up that Descendants is like 3 times slower than Elements, it’s still lesser than a milisecond though in my XML example. Will try to get some better data with bigger XMLs when I have a chance, I’ll also post the accurate times ;)


Would you like to get the most interesting content about C# every Monday?
Sign up to C# Digest and stay up to date!