Traverse XML documents using Linq

Description
Table of Contents
Description

The following XML document is used in the operations section:

<contacts>
  <contact contactId="1">
    <firstName>João</firstName>
    <lastName>Seixas</lastName>
    <addresses>
      <address type="home">
        <street>street 1</street>
        <city>Funchal</city>
        <region>Madeira</region>
        <zip>9020-402</zip>
      </address>
      <address type="work">
        <street>street 1 tecnopolo</street>
        <city>Funchal</city>
        <region>Madeira</region>
        <zip>9020-420</zip>
      </address>
    </addresses>
  </contact>
  <contact contactId="2">
    <firstName>Ana</firstName>
    <lastName>Travassos</lastName>
    <addresses>
      <address type="home">
        <street>street 2</street>
        <city>Funchal</city>
        <region>Madeira</region>
        <zip>9010-402</zip>
      </address>
      <address type="work">
        <street>street 2 tecnopolo</street>
        <city>Funchal</city>
        <region>Madeira</region>
        <zip>9010-420</zip>
      </address>
    </addresses>
  </contact>
    <contact contactId="3">
    <firstName>José</firstName>
    <lastName>Travassos</lastName>
    <address type="home">
        <street>street 3</street>
        <city>Funchal</city>
        <region>Madeira</region>
        <zip>9030-402</zip>
    </address>
    <address type="work">
        <street>street 3 tecnopolo</street>
        <city>Funchal</city>
        <region>Madeira</region>
        <zip>9030-420</zip>
    </address>
  </contact>
</contacts>

 

Operations
Select XML nodes based on a specific element
How to do it
XElement xelement = XElement.Parse(doc);
var query = from c in xelement.Elements("contact")
            where (string)c.Element("firstName") == "João"
			//OR where c.Element("firstName").Value == "João"
            select c;

foreach (XElement item in query)
   Console.WriteLine(item);
<contact contactId="1">
  <firstName>João</firstName>
  <lastName>Seixas</lastName>
  <addresses>
    <address type="home">
      <street>street 1</street>
      <city>Funchal</city>
      <region>Madeira</region>
      <zip>9020-402</zip>
    </address>
    <address type="work">
      <street>street 1 tecnopolo</street>
      <city>Funchal</city>
      <region>Madeira</region>
      <zip>9020-420</zip>
    </address>
  </addresses>
</contact>
Select XML nodes based on a specific attribute
How to do it
var query = from ca in xelement.Elements("contact").Elements("address")
            where (string)ca.Attribute("type") == "work"
			//OR where ca.Attribute("type").Value == "work"
            select ca;

foreach (XElement item in query)
   Console.WriteLine(item);
<address type="work">
  <street>street 1 tecnopolo</street>
  <city>Funchal</city>
  <region>Madeira</region>
  <zip>9020-420</zip>
</address>
<address type="work">
  <street>street 2 tecnopolo</street>
  <city>Funchal</city>
  <region>Madeira</region>
  <zip>9010-420</zip>
</address>
Filter specific XML node by node name
How to do it
string lvValue = element.Nodes.First(n => n.LocalName == "address").InnerXml;
Filter descendant elements
Remarks

The “XElement::Descendants(XName)” method yield elements from the entire source element tree and sub-tree, whereas the “XElement::Elements(XName)” method yield the direct child elements only.

How to do it
var query = from ca in xelement.Descendants("address")
                         select new
                         {
                             street = ca.Element("street").Value,
                             zip = ca.Element("zip").Value
                         };

foreach (var item in query)
{
     Console.WriteLine(item);
}
{ street = street 1, zip = 9020-402 }
{ street = street 1 tecnopolo, zip = 9020-420 }
{ street = street 2, zip = 9010-402 }
{ street = street 2 tecnopolo, zip = 9010-420 }
Filter XML nodes and order by specific elements
How to do it
var query = from c in xelement.Elements("contact")
            let name = (string) c.Element("firstName") + (string) c.Element("lastName")
            orderby name
            select new
            {
                 contact_id = c.Attribute("contactId").Value,
                 contact_name = name
            };

foreach (var item in query)
{
    Console.WriteLine(item);
}
{ contact_id = 2, contact_name = Ana Travassos }
{ contact_id = 1, contact_name = João Seixas }
Filter XML nodes and group by specific element
How to do it
XElement xelement = XElement.Parse(doc);

var query = from c in xelement.Elements("contact")
            group c by c.Element("lastName").Value into g
            orderby g.Key
            select new
            {
		contacts = g,
                number_ofcontacts = g.Count(),
                family_name = g.Key
            };

foreach (var item in query)
{
	Console.WriteLine("family name: {0}, number of contacts: {1}:", item.family_name, item.number_ofcontacts);
    foreach (var item2 in item.contacts)
	{
		Console.WriteLine("\t {0} {1}",item2.Element("firstName").Value,item2.Element("lastName").Value);
	}
}
family name: Seixas, number of contacts: 2:
	 João Seixas
	 José Seixas
family name: Travassos, number of contacts: 1:
	 Ana Travassos