Comparison of XPath and LINQ to XML
XPath and LINQ to XML offer some similar functionality. Both can be used to query an XML tree, returning such results as a collection of elements, a collection of attributes, a collection of nodes, or the value of an element or attribute. However, there are also some differences.
Differences Between XPath and LINQ to XML
XPath does not allow projection of new types. It can only return collections of nodes from the tree, whereas LINQ to XML can execute a query and project an object graph or an XML tree in a new shape. LINQ to XML queries encompass much more functionality and are much more powerful than XPath expressions.
XPath expressions exist in isolation within a string. The C# or Visual Basic compiler cannot help parse the XPath expression at compile time. By contrast, LINQ to XML queries are parsed and compiled by the C# or Visual Basic compiler. The compiler is able to catch many query errors.
XPath results are not strongly typed. In a number of circumstances, the result of evaluating an XPath expression is an object, and it is up to the developer to determine the proper type and cast the result as necessary. By contrast, the projections from a LINQ to XML query are strongly typed.
Result Ordering
The XPath 1.0 Recommendation states that a collection that is the result of evaluating an XPath expression is unordered.
However, when iterating through a collection returned by a LINQ to XML XPath axis method, the nodes in the collection are returned in document order. This is the case even when accessing the XPath axes where predicates are expressed in terms of reverse document order, such as preceding and preceding-sibling.
By contrast, most of the LINQ to XML axes return collections in document order, but two of them, Ancestors and AncestorsAndSelf, return collections in reverse document order. The following table enumerates the axes, and indicates collection order for each:
LINQ to XML axis |
Ordering |
---|---|
XContainer.DescendantNodes |
Document order |
XContainer.Descendants |
Document order |
XContainer.Elements |
Document order |
XContainer.Nodes |
Document order |
XContainer.NodesAfterSelf |
Document order |
XContainer.NodesBeforeSelf |
Document order |
XElement.AncestorsAndSelf |
Reverse document order |
XElement.Attributes |
Document order |
XElement.DescendantNodesAndSelf |
Document order |
XElement.DescendantsAndSelf |
Document order |
XNode.Ancestors |
Reverse document order |
XNode.ElementsAfterSelf |
Document order |
XNode.ElementsBeforeSelf |
Document order |
XNode.NodesAfterSelf |
Document order |
XNode.NodesBeforeSelf |
Document order |
Positional Predicates
Within an XPath expression, positional predicates are expressed in terms of document order for many axes, but are expressed in reverse document order for reverse axes, which are preceding, preceding-sibling, ancestor, and ancestor-or-self. For example, the XPath expression preceding-sibling::*[1] returns the immediately preceding sibling. This is the case even though the final result set is presented in document order.
By contrast, all positional predicates in LINQ to XML are always expressed in terms of the order of the axis. For example, anElement.ElementsBeforeSelf().ToList()[0] returns the first child element of the parent of the queried element, not the immediate preceding sibling. Another example: anElement.Ancestors().ToList()[0] returns the parent element.
Note that the above approach materializes the entire collection. This is not the most efficient way to write that query. It was written in that way to demonstrate the behavior of positional predicates. A more appropriate way to write the same query is to use the First method, as follows: anElement.ElementsBeforeSelf().First().
If you wanted to find the immediately preceding element in LINQ to XML, you would write the following expression:
ElementsBeforeSelf().Last()
Performance Differences
XPath queries that use the XPath functionality in LINQ to XML will not perform as well as LINQ to XML queries.
Comparison of Composition
Composition of a LINQ to XML query is somewhat parallel to composition of an XPath expression, although very different in syntax.
For example, if you have an element in a variable named customers, and you want to find a grandchild element named CompanyName under all child elements named Customer, you would write an XPath expression as follows:
customers.XPathSelectElements("./Customer/CompanyName");
customers.XPathSelectElements("./Customer/CompanyName")
The equivalent LINQ to XML query is:
customers.Element("Customer").Elements("CompanyName");
customers.Element("Customer").Elements("CompanyName")
There are similar parallels for each of the XPath axes.
XPath axis |
LINQ to XML axis |
---|---|
child (the default axis) |
|
Parent (..) |
|
attribute axis (@) |
or |
ancestor axis |
|
ancestor-or-self axis |
|
descendant axis (//) |
or |
descendant-or-self |
or |
following-sibling |
or |
preceding-sibling |
or |
following |
No direct equivalent. |
preceding |
No direct equivalent. |