The LINQ XML objects provide a standard assortment of LINQ functions that make moving data from those objects into IEnumerable objects simple. Using these functions, it’s about as easy to select data from the XML objects as it is from IEnumerable objects such as arrays and lists.
Because the XML objects represent special hierarchical data structures, they also provide methods to help you search those data structures. For example, the XElement object provides a Descendants function that searches the object’s descendants for elements of a certain type.
The following code extracts the x_all XElement object’s Customer descendants. It selects their FirstName and LastName attributes, and the balance saved as each element’s value.
Dim select_all = From cust In x_all.Descendants("Customer")
Order By CDec(cust.Value)
Select FName = cust.Attribute("FirstName").Value,
LName = cust.Attribute("LastName").Value,
Balance = cust.Value
The program can now loop through the select_all object just as it can loop through any other IEnumerable selected by a LINQ query.
The following query selects only customers with a negative balance:
Dim x_neg = From cust In x_all.Descendants("Customer")
Where CDec(cust.Value) < 0
Select FName = cust.Attribute("FirstName").Value,
LName = cust.Attribute("LastName").Value,
Balance = cust.Value
Example program LinqToXml, which is available for download on the book’s website, demonstrates these XML literals containing holes.
The following table describes other methods supported by XElement that a program can use to navigate through an XML hierarchy. Most of the functions return IEnumerable objects that you can then use in LINQ queries.
FUNCTION | RETURNS |
Ancestors | IEnumerable containing all ancestors of the element. |
AncestorsAndSelf | IEnumerable containing this element followed by all of its ancestors. |
Attribute | The element’s attribute with a specific name. |
Attributes | IEnumerable containing the element’s attributes. |
Descendants | IEnumerable containing all descendants of the element. |
DescendantsAndSelf | IEnumerable containing this element followed by all of its descendants. |
DescendantNodes | IEnumerable containing all descendant nodes of the element. These include all nodes such as XElement and XText. |
DescendantNodesAndSelf | IEnumerable containing this element followed by all of its descendant nodes. |
Element | The first child element with a specific name. |
Elements | IEnumerable containing the immediate children of the element. |
ElementsAfterSelf | IEnumerable containing the siblings of the element that come after this element. |
ElementsBeforeSelf | IEnumerable containing the siblings of the element that come before this element. |
Nodes | IEnumerable containing the nodes that are immediate children of the element. These include all nodes such as XElement and XText. |
NodesAfterSelf | IEnumerable containing the sibling nodes of the element that come after this element. |
NodesBeforeSelf | IEnumerable containing the sibling nodes of the element that come before this element. |
Most of these functions that return an IEnumerable take an optional parameter that you can use to indicate the names of the elements to select. For example, if you pass the Descendants function the parameter “Customer,” the function returns only the descendants of the element that are named Customer.
Example program LinqToXmlFunctions, which is available for download on the book’s website, demonstrates these XML functions.
In addition to these functions, Visual Basic’s LINQ query syntax recognizes several axis selectors. In XML, an axis is a “direction” in which you can move from a particular node. These include such directions as the node’s descendants, the node’s immediate children, and the node’s attributes.
The following table gives examples of shorthand expressions for node axes and their functional equivalents.
SHORTHAND | MEANING | EQUIVALENT |
x...<Customer> | Descendants named Customer | x.Descendants(“Customer”) |
x.<Child> | An element named Child that is a child of this node | x.Element(“Child”) |
x.@<FirstName> | The value of the FirstName attribute | x.Attribute(“FirstName”).Value |
x.@FirstName | The value of the FirstName attribute | x.Attribute(“FirstName”).Value |
For example, consider the following XElement literal:
Dim x_all As XElement =
<AllCustomers>
<PositiveBalances>
<Customer FirstName="Dan" LastName="Dump">117.95</Customer>
<Customer FirstName="Ann" LastName="Archer">100.00</Customer>
<Customer FirstName="Carly" LastName="Cant">62.40</Customer>
</PositiveBalances>
<NegativeBalances>
<Customer FirstName="Ben" LastName="Best">-24.54</Customer>
<Customer FirstName="Frank" LastName="Fix">-150.90</Customer>
<Customer FirstName="Edna" LastName="Ever">-192.75</Customer>
</NegativeBalances>
</AllCustomers>
The following code uses axis shorthand to make several different selections:
' Select all Customer descendants of x_all.
Dim desc = x_all.Descendants("Customer") ' Functional version.
Dim desc2 = x_all.<Customer> ' LINQ query version.
' Select Customer descendants of x_all where FirstName attribute is Ben.
Dim ben = From cust In x_all.Descendants("Customer")
Where cust.@FirstName = "Ben"
' Select Customer descendants of x_all where FirstName attribute is Ann.
Dim ann = From cust In x_all.<Customer>
Where cust.@<FirstName> = "Ann"
' Starting at x_all, go to the NegativeBalances node and find
' its descendants that are Customer elements. Select those with
' value less than -50.
Dim neg_desc2 = From cust In x_all.<NegativeBalances>...<Customer>
Where CDec(cust.Value) < -50
Example program LinqAxes, which is available for download on the book’s website, demonstrates these LINQ query XML axes.
Note that IEnumerable objects allow indexing so you can use an index to select a particular item from the results of any of these functions that returns an IEnumerable. For example, the following statement starts at element x_all, goes to descendants named NegativeBalances, gets that element’s Customer children, and then selects the second of them (indexes are numbered starting with zero):
Dim neg_cust1 = x_all.<NegativeBalances>.<Customer>(1)
Together the LINQ XML functions and query axes operators let you explore XML hierarchies quite effectively.
In addition to all of these navigational features, the LINQ XML classes provide the usual assortment of methods for manipulating XML hierarchies. Those functions let you find an element’s parent, add and remove elements, and so forth. For more information, see the online help or the MSDN website.