One last group of extension methods offered by LINQ to XML is related to System.Xml.XPath integration. The System.Xml.XPath.Extensions class provides a few extension methods useful for managing XNode contents via XPath. The first method is CreateNavigator, which returns an XPathNavigator:
public static XPathNavigator CreateNavigator(this XNode node); public static XPathNavigator CreateNavigator(this XNode node, XmlNameTable nameTable);
Internally, it creates an instance of an XNodeNavigator class, which is derived from XPathNavigator and is specifically defined to navigate X* class graphs. The main goal of this method is to make possible the transformation of an XNode using standard System.Xml.Xsl classes. XslCompiledTransform can work with XPathNavigator derived classes as input. Listing 6-39 provides sample code that transforms our customers list using the XSLT we defined in Listing 6-26.
Listing 6-39: XSLT transformation using XslCompiledTransform and the CreateNavigator extension method
XslCompiledTransform xslt = new XslCompiledTransform(); xslt.Load(@"..\..\customerFromSourceToDestination.xslt"); xslt.Transform(sourceXmlCustomers.CreateNavigator(), null, Console.Out);
Another interesting method is XPathEvaluate, which evaluates an XPath rule against the current XNode, returning its value by using the internal class XPathEvaluator. The code in Listing 6-40 selects all attributes of name “name” of all the customers located in Italy.
Listing 6-40: Sample usage of the XPathEvaluate extension method
XElement xmlCustomers = new XElement("customers", from c in customers select new XElement("customer", new XAttribute("name", c.Name), new XAttribute("city", c.City), new XAttribute("country", c.Country))); var result = (IEnumerable<Object>)xmlCustomers.XPathEvaluate( "/customer[@country = 'Italy']/@name"); foreach (var item in result) { Console.WriteLine(item); }
Consider that, in the current beta version of LINQ to XML, the XPathEvaluateMethod cannot determine the result of the XPath query. Therefore, it always returns a value of type Object. It is our responsibility to know and correctly cast the result type. The last two methods to take care of are XPathSelectElement and XPathSelectElements. The former returns the first element corresponding to the XPath expression provided as an argument, and the latter returns the full list of elements matching the expression. Internally, they both use the XPathEvaluator class. The following example selects all the customer elements of customers located in Italy:
var result = xmlCustomers.XPathSelectElements( "/customer[@country = 'Italy']");
This method is sometimes useful for defining LINQ queries that work on a subset of nodes of the source XML graph-in case we need to filter customer elements by the country attribute value. An example is shown in Listing 6-41.
Listing 6-41: Sample LINQ query over the result of the XPathSelectElements extension method
var ordersOfItalianCustomersFromXml = from c in xmlCustomers.XPathSelectElements( "/customer[@country = 'Italy']") let xName = (String)c.Element("name") let xCity = (String)c.Attribute("city") join o in orders on xName equals o.Name orderby xName select new { Name = xName, City = xCity, IdProduct = o.IdProduct, Quantity = o.Quantity };
Remember that XPath rules are checked at run time, not at compile time. Therefore, be careful when defining them within LINQ queries. Consider also that like many other LINQ extension methods, the XPath methods we have just evaluated also support deferred query evaluation. Once again, keep in mind that every time we use queries defined using these methods, the result is refreshed by rescanning the source XML graph, unless you copy it yourself by using any of the extension methods provided by LINQ queries for doing that (such as ToList, ToArray, ToDictionary, or ToLookup).