Tải bản đầy đủ (.pdf) (52 trang)

Apress pro LINQ Language Integrated Query in C# 2008 phần 6 ppt

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (822.18 KB, 52 trang )

CHAPTER 8 ■ LINQ TO XML OPERATORS
275
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Descendants("FirstName");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}
// Now, I will display the ancestor elements for each source element.
foreach (XElement element in elements.Ancestors())
{
Console.WriteLine("Ancestor element: {0}", element.Name);
}
In the previous example, first, I create an XML document. Next, I generate a sequence of FirstName
elements. Remember, this Ancestors method is called on a sequence of nodes, not on a single node,
so I need a sequence on which to call it. Because I want to be able to display the names of the nodes
for identification purposes, I actually build a sequence of elements because elements have names
but nodes do not. I then enumerate through the sequence displaying the source elements just so you
can see the source sequence. Then, I enumerate on the elements returned from the Ancestors method
and display them. Here are the results:
Source element: FirstName : value = Joe
Source element: FirstName : value = Ewan
Ancestor element: BookParticipant
Ancestor element: BookParticipants
Ancestor element: BookParticipant


Ancestor element: BookParticipants
As you can see, it displays the two source sequence elements, the two FirstName elements. It
then displays the ancestors for each of those two elements.
So using the Ancestors operator, I am able to retrieve all of the ancestor elements for each
node in a sequence of nodes. In this case, my sequence is a sequence of elements, but that is alright
because an element is derived from a node. Remember, do not confuse the Ancestors operator
that is called on a sequence of nodes, which I just demonstrated, with the Ancestors method I
cover in the previous chapter.
Now this example is not quite as impressive as it could be because I needed to expand the code
for demonstration purposes. For example, I wanted to capture the sequence of FirstName elements,
because I wanted to display them so you could see the source elements in the output. So the state-
ment containing the call to the Descendants method and the subsequent foreach block are for this
purpose. Then in the second foreach loop, I call the Ancestors operator and display each ancestor
element. In reality, in that second foreach loop, I could have called the Ancestors method from the
previous chapter on each element in the sequence of FirstName elements and not even called the
Ancestors operator I am demonstrating. Listing 8-2 is an example demonstrating what I could have
done, which would have accomplished the same result, but without even using the Ancestors operator.
Rattz_789-3.book Page 275 Tuesday, October 16, 2007 2:21 PM
276
CHAPTER 8
■ LINQ TO XML OPERATORS
Listing 8-2. The Same Results as Listing 8-1 But Without Calling the Ancestors Operator
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),

new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Descendants("FirstName");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}
foreach (XElement element in elements)
{
// Call the Ancestors method on each element.
foreach(XElement e in element.Ancestors())
// Now, I will display the ancestor elements for each source element.
Console.WriteLine("Ancestor element: {0}", e.Name);
}
The difference between this example and the previous is that instead of calling the Ancestors
operator on the elements sequence in the foreach loop, I just loop on each element in the sequence
and call the Ancestors method on it. In this example, I never call the Ancestors operator; I merely call
the Ancestors method from the previous chapter. This code produces the same output though:
Source element: FirstName : value = Joe
Source element: FirstName : value = Ewan
Ancestor element: BookParticipant
Ancestor element: BookParticipants
Ancestor element: BookParticipant
Ancestor element: BookParticipants
However, thanks to the Ancestors operator and the conciseness of LINQ, this query can be
combined into a single, more concise statement as demonstrated in Listing 8-3.
Listing 8-3. A More Concise Example of Calling the First Ancestors Prototype

XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
Rattz_789-3.book Page 276 Tuesday, October 16, 2007 2:21 PM
CHAPTER 8 ■ LINQ TO XML OPERATORS
277
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
foreach (XElement element in
xDocument.Element("BookParticipants").Descendants("FirstName").Ancestors())
{
Console.WriteLine("Ancestor element: {0}", element.Name);
}
In this example, I cut right to the chase and call the Ancestors operator on the sequence of elements
returned by the Descendants method. So the Descendants method returns a sequence of elements,
and the Ancestors operator will return a sequence of elements containing all ancestors of every
element in the sequence it is called on.
Since this code is meant to be more concise, it does not display the FirstName elements as the
two previous examples did. However, the ancestor elements should be the same. Let’s verify that
they are:
Ancestor element: BookParticipant
Ancestor element: BookParticipants
Ancestor element: BookParticipant
Ancestor element: BookParticipants
And they are! In your production code, you would probably opt for a more concise query like the

one I just presented. However, in this chapter, the examples will be more verbose, like Listing 8-1, for
demonstration purposes.
To demonstrate the second Ancestors prototype, I will use the same basic code as Listing 8-1,
except I will change the call to the Ancestors operator so that it includes the parameter BookParticipant
so that I only get the elements matching that name. That code looks like Listing 8-4.
Listing 8-4. Calling the Second Ancestors Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Descendants("FirstName");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}
Rattz_789-3.book Page 277 Tuesday, October 16, 2007 2:21 PM
278
CHAPTER 8
■ LINQ TO XML OPERATORS
// Now, I will display the ancestor elements for each source element.
foreach (XElement element in elements.Ancestors("BookParticipant"))

{
Console.WriteLine("Ancestor element: {0}", element.Name);
}
The results now should only include the BookParticipant elements and of course the source
elements, but the two BookParticipants elements that are displayed in the first prototype’s example
should now be gone:
Source element: FirstName : value = Joe
Source element: FirstName : value = Ewan
Ancestor element: BookParticipant
Ancestor element: BookParticipant
And they are.
AncestorsAndSelf
The AncestorsAndSelf operator can be called on a sequence of elements and returns a sequence
containing the ancestor elements of each source element and the source element itself. This operator is
just like the Ancestors operator except for the fact that it can only be called on elements, as opposed
to nodes, and also includes each source element in the returned sequence of ancestor elements.
Prototypes
The AncestorsAndSelf operator has two prototypes.
The First AncestorsAndSelf Prototype
public static IEnumerable<XElement> AncestorsAndSelf (
this IEnumerable<XElement> source
)
This version of the operator can be called on a sequence of elements and returns a sequence of
elements containing each source element itself and its ancestor elements.
The Second AncestorsAndSelf Prototype
public static IEnumerable<XElement> AncestorsAndSelf<T> (
this IEnumerable<XElement> source,
XName name
)
This version is like the first, except a name is passed and only those source elements and its

ancestors matching the specified name are returned in the output sequence.
Examples
For an example of the first AncestorsAndSelf prototype, I will use the same basic example I used for
the first Ancestors prototype, except I will call the AncestorsAndSelf operator instead of the Ancestors
operator, as shown in Listing 8-5.
Rattz_789-3.book Page 278 Tuesday, October 16, 2007 2:21 PM
CHAPTER 8 ■ LINQ TO XML OPERATORS
279
Listing 8-5. Calling the First AncestorsAndSelf Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Descendants("FirstName");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}
// Now, I will display the ancestor elements for each source element.
foreach (XElement element in elements.AncestorsAndSelf())
{

Console.WriteLine("Ancestor element: {0}", element.Name);
}
Just as with the first Ancestors prototype, first, I create an XML document. Next, I generate a
sequence of FirstName elements. Remember, this AncestorsAndSelf method is called on a sequence
of elements, not on a single element, so I need a sequence on which to call it. I then enumerate through
the sequence displaying the source elements just so you can see the source sequence. Then, I enumerate
on the elements returned from the AncestorsAndSelf method and display them.
If this works as I expect, the results should be the same as the results from the first Ancestors
prototype’s example, except now the FirstName elements should be included in the output. Here are
the results:
Source element: FirstName : value = Joe
Source element: FirstName : value = Ewan
Ancestor element: FirstName
Ancestor element: BookParticipant
Ancestor element: BookParticipants
Ancestor element: FirstName
Ancestor element: BookParticipant
Ancestor element: BookParticipants
For an example of the second AncestorsAndSelf prototype, I will use the same basic example
that I used in the example for the second Ancestors prototype, except, of course, I will change the call
from the Ancestors method to the AncestorsAndSelf method, as shown in Listing 8-6.
Rattz_789-3.book Page 279 Tuesday, October 16, 2007 2:21 PM
280
CHAPTER 8
■ LINQ TO XML OPERATORS
Listing 8-6. Calling the Second AncestorsAndSelf Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),

new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Descendants("FirstName");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}
// Now, I will display the ancestor elements for each source element.
foreach (XElement element in elements.AncestorsAndSelf("BookParticipant"))
{
Console.WriteLine("Ancestor element: {0}", element.Name);
}
Now, I should only receive the elements named BookParticipant. Here are the results:
Source element: FirstName : value = Joe
Source element: FirstName : value = Ewan
Ancestor element: BookParticipant
Ancestor element: BookParticipant
Notice that the displayed output from the AncestorsAndSelf method is just the BookParticipant
elements, because they are the only elements matching the name I passed. I didn’t even get the source
elements themselves, because they didn’t match the name. So the function worked as defined.
Call me crazy, but this prototype of the operator seems fairly useless to me. How many levels of
elements are you going to have in an XML tree with the same name? If you don’t answer at least two,
how will this method ever return the self elements and any ancestor elements? It just doesn’t seem

likely to me. Yes, I know; I like symmetrical APIs too.
Attributes
The Attributes operator can be called on a sequence of elements and returns a sequence containing
the attributes of each source element.
Prototypes
The Attributes operator has two prototypes.
Rattz_789-3.book Page 280 Tuesday, October 16, 2007 2:21 PM
CHAPTER 8 ■ LINQ TO XML OPERATORS
281
The First Attributes Prototype
public static IEnumerable<XAttribute> Attributes (
this IEnumerable<XElement> source
)
This version of the operator can be called on a sequence of elements and returns a sequence of
attributes containing all the attributes for each source element.
The Second Attributes Prototype
public static IEnumerable<XAttribute> Attributes (
this IEnumerable<XElement> source,
XName name
)
This version of the operator is like the first, except only those attributes matching the specified
name will be returned in the sequence of attributes.
Examples
For an example of the first Attributes prototype, I will build the same XML tree I have been building
for the previous examples. However, the sequence of source elements I generate will be a little different
because I need a sequence of elements with attributes. So I’ll generate a sequence of the BookParticipant
elements and work from there, as shown in Listing 8-7.
Listing 8-7. Calling the First Attributes Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",

new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Elements("BookParticipant");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}
// Now, I will display each source element's attributes.
foreach (XAttribute attribute in elements.Attributes())
{
Console.WriteLine("Attribute: {0} : value = {1}",
attribute.Name, attribute.Value);
}
Rattz_789-3.book Page 281 Tuesday, October 16, 2007 2:21 PM
282
CHAPTER 8
■ LINQ TO XML OPERATORS
Once I obtain the sequence of BookParticipant elements, I display the source sequence. Then,
I call the Attributes operator on the source sequence and display the attributes in the sequence
returned by the Attributes operator. Here are the results:
Source element: BookParticipant : value = JoeRattz

Source element: BookParticipant : value = EwanBuckingham
Attribute: type : value = Author
Attribute: type : value = Editor
As you can see, the attributes are retrieved. For an example of the second Attributes prototype,
I will use the same basic example as the previous, except I will specify a name that the attributes must
match to be returned by the Attributes operator, as shown in Listing 8-8.
Listing 8-8. Calling the Second Attributes Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Elements("BookParticipant");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}
// Now, I will display each source element's attributes.
foreach (XAttribute attribute in elements.Attributes("type"))
{
Console.WriteLine("Attribute: {0} : value = {1}",
attribute.Name, attribute.Value);

}
In the previous code, I specify that the attributes must match the name type. So this should
return the same output as the previous example. Pressing Ctrl+F5 returns the following:
Source element: BookParticipant : value = JoeRattz
Source element: BookParticipant : value = EwanBuckingham
Attribute: type : value = Author
Attribute: type : value = Editor
So I did get the results I expected. Had I specified the name as Type so that the first letter is capital-
ized, the two attributes would not have been displayed because the Attributes operator would not have
returned those attributes from the source sequence. That demonstrates the case of when the name
Rattz_789-3.book Page 282 Tuesday, October 16, 2007 2:21 PM
CHAPTER 8 ■ LINQ TO XML OPERATORS
283
doesn’t match, as well as the fact that the name is case-sensitive, which isn’t that surprising since
XML is case-sensitive.
DescendantNodes
The DescendantNodes operator can be called on a sequence of elements and returns a sequence
containing the descendant nodes of each element or document.
Prototypes
The DescendantNodes operator has one prototype.
The Only DescendantNodes Prototype
public static IEnumerable<XNode> DescendantNodes<T> (
this IEnumerable<T> source
) where T : XContainer
This version can be called on a sequence of elements or documents and returns a sequence of
nodes containing each source element’s or document’s descendant nodes.
This is different from the XContainer.DescendantNodes method in that this method is called on
a sequence of elements or documents, as opposed to a single element or document.
Examples
For this example, I will build the same XML tree I have used for the previous examples, except I will

also add a comment to the first BookParticipant element. This is to have at least one node get returned
that is not an element. When I build my source sequence of elements, I want some elements that
have some descendants, so I will build my source sequence with the BookParticipant elements since
they have some descendants, as shown in Listing 8-9.
Listing 8-9. Calling the Only DescendantNodes Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XComment("This is a new author."),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Elements("BookParticipant");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}
Rattz_789-3.book Page 283 Tuesday, October 16, 2007 2:21 PM
284
CHAPTER 8
■ LINQ TO XML OPERATORS
// Now, I will display each source element's descendant nodes.
foreach (XNode node in elements.DescendantNodes())

{
Console.WriteLine("Descendant node: {0}", node);
}
As is typical with the examples in this section, I built my XML tree and a source sequence of
elements. In this case, the source sequence contains the BookParticipant elements. I then call the
DescendantNodes operator on the source sequence and display the results:
Source element: BookParticipant : value = JoeRattz
Source element: BookParticipant : value = EwanBuckingham
Descendant node: <! This is a new author >
Descendant node: <FirstName>Joe</FirstName>
Descendant node: Joe
Descendant node: <LastName>Rattz</LastName>
Descendant node: Rattz
Descendant node: <FirstName>Ewan</FirstName>
Descendant node: Ewan
Descendant node: <LastName>Buckingham</LastName>
Descendant node: Buckingham
Notice that not only did I get my descendant elements, I got my comment node as well. Also
notice that for each element in the XML document, I ended up with two nodes. For example, there is
a node whose value is "<FirstName>Joe</FirstName>" and a node whose value is "Joe". The first node
in the pair is the FirstName element. The second node is the XText node for that element. I bet you
had forgotten about those automatically created XText objects. I know I did, but there they are.
DescendantNodesAndSelf
The DescendantNodesAndSelf operator can be called on a sequence of elements and returns a sequence
containing each source element itself and each source element’s descendant nodes.
Prototypes
The DescendantNodesAndSelf operator has one prototype.
The Only DescendantNodesAndSelf Prototype
public static IEnumerable<XNode> DescendantNodesAndSelf (
this IEnumerable<XElement> source

)
This version is called on a sequence of elements and returns a sequence of nodes containing
each source element itself and each source element’s descendant nodes.
Examples
For this example, I will use the same example used for the DescendantNodes operator, except I will
call the DescendantNodesAndSelf operator, as shown in Listing 8-10.
Rattz_789-3.book Page 284 Tuesday, October 16, 2007 2:21 PM
CHAPTER 8 ■ LINQ TO XML OPERATORS
285
Listing 8-10. Calling the Only DescendantNodesAndSelf Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XComment("This is a new author."),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Elements("BookParticipant");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}
// Now, I will display each source element's descendant nodes.

foreach (XNode node in elements.DescendantNodesAndSelf())
{
Console.WriteLine("Descendant node: {0}", node);
}
So the question is, will the output be the same as the output for the DescendantNodes example
except that the source elements will be included too? You bet:
Source element: BookParticipant : value = JoeRattz
Source element: BookParticipant : value = EwanBuckingham
Descendant node: <BookParticipant type="Author">
<! This is a new author >
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
</BookParticipant>
Descendant node: <! This is a new author >
Descendant node: <FirstName>Joe</FirstName>
Descendant node: Joe
Descendant node: <LastName>Rattz</LastName>
Descendant node: Rattz
Descendant node: <BookParticipant type="Editor">
<FirstName>Ewan</FirstName>
<LastName>Buckingham</LastName>
</BookParticipant>
Descendant node: <FirstName>Ewan</FirstName>
Descendant node: Ewan
Descendant node: <LastName>Buckingham</LastName>
Descendant node: Buckingham
Rattz_789-3.book Page 285 Tuesday, October 16, 2007 2:21 PM
286
CHAPTER 8
■ LINQ TO XML OPERATORS

Not only did I get the BookParticipant elements themselves and their descendants, I got the single
node that is not an element, the comment. This is in contrast to the Descendants and DescendantsAndSelf
operators I cover next, which will omit the nodes that are not elements.
Descendants
The Descendants operator can be called on a sequence of elements or documents and returns a
sequence of elements containing each source element’s or document’s descendant elements.
Prototypes
The Descendants operator has two prototypes.
The First Descendants Prototype
public static IEnumerable<XElement> Descendants<T> (
this IEnumerable<T> source
) where T : XContainer
This version is called on a sequence of elements or documents and returns a sequence of elements
containing each source element’s or document’s descendant elements.
This is different from the XContainer.Descendants method in that this method is called on a
sequence of elements or documents, as opposed to a single element or document.
The Second Descendants Prototype
public static IEnumerable<XElement> Descendants<T> (
this IEnumerable<T> source,
XName name
) where T : XContainer
This version is like the first, except only those elements matching the specified name are returned
in the output sequence.
Examples
For the example of the first prototype, I will basically use the same example I used for the
DescendantNodes operator, except I will call the Descendants operator instead. The output should be
the same, except there should not be any nodes that are not elements. This means you should not
see the comment in the output. Listing 8-11 shows the code.
Listing 8-11. Calling the First Descendants Prototype
XDocument xDocument = new XDocument(

new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XComment("This is a new author."),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
Rattz_789-3.book Page 286 Tuesday, October 16, 2007 2:21 PM
CHAPTER 8 ■ LINQ TO XML OPERATORS
287
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Elements("BookParticipant");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}
// Now, I will display each source element's descendant elements.
foreach (XElement element in elements.Descendants())
{
Console.WriteLine("Descendant element: {0}", element);
}
This example is basically like all of the previous except you should only see the descendant
elements of the two BookParticipant elements. The results of this example are the following:
Source element: BookParticipant : value = JoeRattz
Source element: BookParticipant : value = EwanBuckingham

Descendant element: <FirstName>Joe</FirstName>
Descendant element: <LastName>Rattz</LastName>
Descendant element: <FirstName>Ewan</FirstName>
Descendant element: <LastName>Buckingham</LastName>
Comparing these results to that of the DescendantNodes operator example, I notice some differ-
ences I did not initially anticipate. Sure, the descendants are labeled as elements instead of nodes,
and the comment is not there, but additionally, the descendant nodes such as Joe and Rattz are
missing as well. Oh yeah, those nodes are not elements either; they are XText objects. The LINQ to
XML API handles the text nodes so seamlessly that it is easy to forget about them.
For an example of the second prototype, I will use the same code as the first example except
specify a name that the descendant elements must match to be returned by the second prototype of
the Descendants operator, as shown in Listing 8-12.
Listing 8-12. Calling the Second Descendants Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XComment("This is a new author."),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Elements("BookParticipant");
Rattz_789-3.book Page 287 Tuesday, October 16, 2007 2:21 PM
288
CHAPTER 8
■ LINQ TO XML OPERATORS

// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}
// Now, I will display each source element's descendant elements.
foreach (XElement element in elements.Descendants("LastName"))
{
Console.WriteLine("Descendant element: {0}", element);
}
The results of this example are the following:
Source element: BookParticipant : value = JoeRattz
Source element: BookParticipant : value = EwanBuckingham
Descendant element: <LastName>Rattz</LastName>
Descendant element: <LastName>Buckingham</LastName>
As you would expect, only the LastName elements are returned.
DescendantsAndSelf
The DescendantsAndSelf operator can be called on a sequence of elements and returns a sequence
containing each source element and its descendant elements.
Prototypes
The DescendantsAndSelf operator has two prototypes.
The First DescendantsAndSelf Prototype
public static IEnumerable<XElement> DescendantsAndSelf (
this IEnumerable<XElement> source
)
This version is called on a sequence of elements and returns a sequence of elements containing
each source element and its descendant elements.
The Second DescendantsAndSelf Prototype
public static IEnumerable<XElement> DescendantsAndSelf (

this IEnumerable<XElement> source,
XName name
)
This version is like the first, except only those elements matching the specified name are returned
in the output sequence.
Examples
For this example, I will use the same code as the example for the first prototype of the Descendants
operator, except I will call the DescendantsAndSelf operator, as shown in Listing 8-13.
Rattz_789-3.book Page 288 Tuesday, October 16, 2007 2:21 PM
CHAPTER 8 ■ LINQ TO XML OPERATORS
289
Listing 8-13. Calling the First DescendantsAndSelf Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XComment("This is a new author."),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Elements("BookParticipant");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);

}
// Now, I will display each source element's descendant elements.
foreach (XElement element in elements.DescendantsAndSelf())
{
Console.WriteLine("Descendant element: {0}", element);
}
Now, you should see all the descendant elements and the source elements themselves. The
results of this example are the following:
Source element: BookParticipant : value = JoeRattz
Source element: BookParticipant : value = EwanBuckingham
Descendant element: <BookParticipant type="Author">
<! This is a new author >
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
</BookParticipant>
Descendant element: <FirstName>Joe</FirstName>
Descendant element: <LastName>Rattz</LastName>
Descendant element: <BookParticipant type="Editor">
<FirstName>Ewan</FirstName>
<LastName>Buckingham</LastName>
</BookParticipant>
Descendant element: <FirstName>Ewan</FirstName>
Descendant element: <LastName>Buckingham</LastName>
So the output is the same as the first prototype for the Descendants operator, except it does include
the source elements themselves, the BookParticipant elements. Don’t let the existence of the comment
in the results fool you. It is not there because the comment was returned by the DescendantsAndSelf
operator; it is there because I display the BookParticipant element, which was returned by the operator.
For the second DescendantsAndSelf prototype, I will use the same example as the first prototype,
except specify a name the element must match to be returned, as shown in Listing 8-14.
Rattz_789-3.book Page 289 Tuesday, October 16, 2007 2:21 PM

290
CHAPTER 8
■ LINQ TO XML OPERATORS
Listing 8-14. Calling the Second DescendantsAndSelf Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XComment("This is a new author."),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Elements("BookParticipant");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}
// Now, I will display each source element's descendant elements.
foreach (XElement element in elements.DescendantsAndSelf("LastName"))
{
Console.WriteLine("Descendant element: {0}", element);
}
The results of this example are the following:
Source element: BookParticipant : value = JoeRattz

Source element: BookParticipant : value = EwanBuckingham
Descendant element: <LastName>Rattz</LastName>
Descendant element: <LastName>Buckingham</LastName>
The results only include the descendant elements that match the name I specified. There isn’t
much evidence that I called the DescendantsAndSelf operator, as opposed to the Descendants operator,
since the source elements were not returned due to their name not matching the specified name.
Again, as with all the operators that return elements from multiple levels of the XML tree that accept
a name argument that the elements must match to be returned, it just doesn’t seem likely that you
will need the AndSelf versions of the operators, because you probably wouldn’t have that many levels
of elements having the same name.
Elements
The Elements operator can be called on a sequence of elements or documents and returns a sequence of
elements containing each source element’s or document’s child elements.
This operator is different than the Descendants operator, because the Elements operator only
returns the immediate child elements of each element in the source sequence of elements, whereas
the Descendants operator recursively returns all child elements until the end of each tree is reached.
Rattz_789-3.book Page 290 Tuesday, October 16, 2007 2:21 PM
CHAPTER 8 ■ LINQ TO XML OPERATORS
291
Prototypes
The Elements operator has two prototypes.
The First Elements Prototype
public static IEnumerable<XElement> Elements<T> (
this IEnumerable<T> source
) where T : XContainer
This version is called on a sequence of elements or documents and returns a sequence of elements
containing each source element’s or document’s child elements.
This is different from the XContainer.Elements method in that this method is called on a sequence
of elements or documents, as opposed to a single element or document.
The Second Elements Prototype

public static IEnumerable<XElement> Elements<T> (
this IEnumerable<T> source,
XName name
) where T : XContainer
This version is like the first, except only those elements matching the specified name are returned
in the output sequence.
Examples
By now, you probably know the drill. For an example of the first prototype, I will use the same basic
example as the DescendantsAndSelf operator used, except I will call the Elements operator instead, as
shown in Listing 8-15.
Listing 8-15. Calling the First Elements Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XComment("This is a new author."),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Elements("BookParticipant");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}

Rattz_789-3.book Page 291 Tuesday, October 16, 2007 2:21 PM
292
CHAPTER 8
■ LINQ TO XML OPERATORS
// Now, I will display each source element's elements.
foreach (XElement element in elements.Elements())
{
Console.WriteLine("Child element: {0}", element);
}
As in the previous examples, I build my XML tree, obtain a sequence of source elements, display
each source element, retrieve a sequence of each source element’s child elements, and display the
child elements:
Source element: BookParticipant : value = JoeRattz
Source element: BookParticipant : value = EwanBuckingham
Child element: <FirstName>Joe</FirstName>
Child element: <LastName>Rattz</LastName>
Child element: <FirstName>Ewan</FirstName>
Child element: <LastName>Buckingham</LastName>
That example returns all child elements. To retrieve just those matching a specific name, I use
the second prototype of the Elements operator, as shown in Listing 8-16.
Listing 8-16. Calling the Second Elements Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XComment("This is a new author."),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),

new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Elements("BookParticipant");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}
// Now, I will display each source element's elements.
foreach (XElement element in elements.Elements("LastName"))
{
Console.WriteLine("Child element: {0}", element);
}
Rattz_789-3.book Page 292 Tuesday, October 16, 2007 2:21 PM
CHAPTER 8 ■ LINQ TO XML OPERATORS
293
Now, I should only get the child elements matching the name LastName:
Source element: BookParticipant : value = JoeRattz
Source element: BookParticipant : value = EwanBuckingham
Child element: <LastName>Rattz</LastName>
Child element: <LastName>Buckingham</LastName>
That works just as expected.
InDocumentOrder
The InDocumentOrder operator can be called on a sequence of nodes and returns a sequence containing
each source node’s child nodes in document order.
Prototypes
The InDocumentOrder operator has one prototype.
The Only InDocumentOrder Prototype

public static IEnumerable<T> InDocumentOrder<T> (
this IEnumerable<T> source
) where T : XNode
This version is called on a sequence of a specified type, which must be nodes or some type
derived from nodes, and returns a sequence of that same type containing each source node’s child
nodes in document order.
Examples
This is a fairly odd operator. For this example, I need a source sequence of nodes. Since I want to see
some nodes that are not elements in addition to elements, I will build a sequence of nodes that are
the child nodes of the BookParticipant elements. I do this because one of them has a comment,
which is a node, but not an element. My source is shown in Listing 8-17.
Listing 8-17. Calling the Only InDocumentOrder Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XComment("This is a new author."),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
Rattz_789-3.book Page 293 Tuesday, October 16, 2007 2:21 PM
294
CHAPTER 8
■ LINQ TO XML OPERATORS
IEnumerable<XNode> nodes =
xDocument.Element("BookParticipants").Elements("BookParticipant").
Nodes().Reverse();

// First, I will display the source nodes.
foreach (XNode node in nodes)
{
Console.WriteLine("Source node: {0}", node);
}
// Now, I will display each source node's child nodes.
foreach (XNode node in nodes.InDocumentOrder())
{
Console.WriteLine("Ordered node: {0}", node);
}
As you can see in the previous code, I build my XML tree. When I retrieve my source sequence, I
get the BookParticipant element’s child nodes by calling the Nodes operator, and then I call the Reverse
Standard Query Operator. If you recall from Part 2 of this book about LINQ to Objects, the Reverse
operator will return a sequence where entries in the input sequence have had their order reversed.
So now I have a sequence of nodes that are not in the original order. I take this additional step of
altering the order so that when I call the InDocumentOrder operator, a difference can be detected.
Then I display the disordered source nodes, call the InDocumentOrder operator, and display the results.
Here they are:
Source node: <LastName>Buckingham</LastName>
Source node: <FirstName>Ewan</FirstName>
Source node: <LastName>Rattz</LastName>
Source node: <FirstName>Joe</FirstName>
Source node: <! This is a new author >
Ordered node: <! This is a new author >
Ordered node: <FirstName>Joe</FirstName>
Ordered node: <LastName>Rattz</LastName>
Ordered node: <FirstName>Ewan</FirstName>
Ordered node: <LastName>Buckingham</LastName>
As you can see, the source nodes are in the reverse order that I built them in, and the ordered
nodes are back in the original order. Cool, but odd.

Nodes
The Nodes operator can be called on a sequence of elements or documents and returns a sequence
of nodes containing each source element’s or document’s child nodes.
This operator is different than the DescendantNodes operator in that the Nodes operator only
returns the immediate child elements of each element in the source sequence of elements, whereas
the DescendantNodes operator recursively returns all child nodes until the end of each tree is reached.
Rattz_789-3.book Page 294 Tuesday, October 16, 2007 2:21 PM
CHAPTER 8 ■ LINQ TO XML OPERATORS
295
Prototypes
The Nodes operator has one prototype.
The Only Nodes Prototype
public static IEnumerable<XNode> Nodes<T> (
this IEnumerable<T> source
) where T : XContainer
This version is called on a sequence of elements or documents and returns a sequence of nodes
containing each source element’s or document’s child nodes.
This is different from the XContainer.Nodes method in that this method is called on a sequence
of elements or documents, as opposed to a single element or document.
Examples
For this example, I will build my typical XML tree and build a source sequence of BookParticipant
elements. I will display each of them, and then I will return the child nodes of each source element
and display them, as shown in Listing 8-18.
Listing 8-18. Calling the Only Nodes Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XComment("This is a new author."),
new XElement("FirstName", "Joe"),

new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XElement> elements =
xDocument.Element("BookParticipants").Elements("BookParticipant");
// First, I will display the source elements.
foreach (XElement element in elements)
{
Console.WriteLine("Source element: {0} : value = {1}",
element.Name, element.Value);
}
// Now, I will display each source element's child nodes.
foreach (XNode node in elements.Nodes())
{
Console.WriteLine("Child node: {0}", node);
}
Rattz_789-3.book Page 295 Tuesday, October 16, 2007 2:21 PM
296
CHAPTER 8
■ LINQ TO XML OPERATORS
Since this operator returns the child nodes, as opposed to elements, the output should have the
comment of the first BookParticipant element in the results:
Source element: BookParticipant : value = JoeRattz
Source element: BookParticipant : value = EwanBuckingham
Child node: <! This is a new author >
Child node: <FirstName>Joe</FirstName>
Child node: <LastName>Rattz</LastName>
Child node: <FirstName>Ewan</FirstName>

Child node: <LastName>Buckingham</LastName>
The results display each source element’s child nodes. Notice that because only the immediate
child nodes are retrieved, I didn’t get the XText nodes that are children of each FirstName and LastName
element, as I did in the DescendantNodes operator example.
Remove
The Remove operator can be called on a sequence of nodes or attributes to remove them. This method
will cache a copy of the nodes or attributes in a List to eliminate the Halloween problem discussed
in the previous chapter.
Prototypes
The Remove operator has two prototypes.
The First Remove Prototype
public static void Remove (
this IEnumerable<XAttribute> source
)
This version is called on a sequence of attributes and removes all attributes in the source sequence.
The Second Remove Prototype
public static void Remove<T> (
this IEnumerable<T> source
) where T : XNode
This version is called on a sequence of a specified type, which must be nodes or some type
derived from nodes, and removes all nodes in the source sequence.
Examples
Since the first prototype is for removing attributes, I need a sequence of attributes. So I will build my
standard XML tree and retrieve a sequence of the BookParticipant element’s attributes. I will display
each source attribute, then call the Remove operator on the sequence of source attributes. Then, just
to prove it worked, I will display the entire XML document, and the attributes will be gone, as shown
in Listing 8-19.
Rattz_789-3.book Page 296 Tuesday, October 16, 2007 2:21 PM
CHAPTER 8 ■ LINQ TO XML OPERATORS
297

Listing 8-19. Calling the First Remove Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XComment("This is a new author."),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XAttribute> attributes =
xDocument.Element("BookParticipants").Elements("BookParticipant").Attributes();
// First, I will display the source attributes.
foreach (XAttribute attribute in attributes)
{
Console.WriteLine("Source attribute: {0} : value = {1}",
attribute.Name, attribute.Value);
}
attributes.Remove();
// Now, I will display the XML document.
Console.WriteLine(xDocument);
Will it work? Let’s see:
Source attribute: type : value = Author
Source attribute: type : value = Editor
<BookParticipants>
<BookParticipant>
<! This is a new author >
<FirstName>Joe</FirstName>

<LastName>Rattz</LastName>
</BookParticipant>
<BookParticipant>
<FirstName>Ewan</FirstName>
<LastName>Buckingham</LastName>
</BookParticipant>
</BookParticipants>
So far, all is good. Now, I’ll try the second prototype. For this example, instead of merely obtaining
a sequence of nodes and removing them, I’ll show something that might be a little more interesting.
I’ll get a sequence of the comments of some particular elements and remove just those, as shown in
Listing 8-20.
Rattz_789-3.book Page 297 Tuesday, October 16, 2007 2:21 PM
298
CHAPTER 8
■ LINQ TO XML OPERATORS
Listing 8-20. Calling the Second Remove Prototype
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XComment("This is a new author."),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
IEnumerable<XComment> comments =
xDocument.Element("BookParticipants").Elements("BookParticipant").
Nodes().OfType<XComment>();

// First, I will display the source comments.
foreach (XComment comment in comments)
{
Console.WriteLine("Source comment: {0}", comment);
}
comments.Remove();
// Now, I will display the XML document.
Console.WriteLine(xDocument);
In this example, when building my source sequence, I retrieve the child nodes of each
BookParticipant element. I could just call the Remove operator on that sequence, and then all the child
nodes of each BookParticipant element would be gone. But instead, to spice it up, I call the OfType
Standard Query Operator. If you recall from Part 2 of this book on LINQ to Objects, this operator will
return only the objects in the input sequence matching the type specified. By calling the OfType oper-
ator and specifying a type of XComment, I get a sequence of just the comments. Then, I call the Remove
method on the comments. The results should be that the original document is missing the one comment
that it initially had:
Source comment: <! This is a new author >
<BookParticipants>
<BookParticipant type="Author">
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
</BookParticipant>
<BookParticipant type="Editor">
<FirstName>Ewan</FirstName>
<LastName>Buckingham</LastName>
</BookParticipant>
</BookParticipants>
That worked like a charm. Look how handy the OfType operator is and how I can integrate it into
the LINQ to XML query. That seems like it could be very useful.
Rattz_789-3.book Page 298 Tuesday, October 16, 2007 2:21 PM

CHAPTER 8 ■ LINQ TO XML OPERATORS
299
Summary
In the previous chapter, I covered the new LINQ to XML API that allows you to create, modify, save,
and load XML trees. Notice I said trees as opposed to documents, because with LINQ to XML, docu-
ments are no longer a requirement. In that chapter, I demonstrated how to query a single node or
element for nodes and elements hierarchically related to it. In this chapter, I covered doing the same
thing with sequences of nodes or elements using the LINQ to XML operators. I hope I have made it
clear how to perform elementary queries on XML trees using LINQ to XML. I believe that this new
XML API will prove to be quite useful for querying XML data. In particular, the way the Standard Query
Operators can be mingled with LINQ to XML operators lends itself to quite elegant and powerful queries.
At this point, I have covered just about all there is to know about the building blocks needed for
performing LINQ to XML queries. In the next chapter, I provide some slightly more complex queries
and cover some of the remaining XML necessities such as validation and transformation.
Rattz_789-3.book Page 299 Tuesday, October 16, 2007 2:21 PM

×