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

Apress pro LINQ Language Integrated Query in C# 2008 phần 5 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 (915.44 KB, 57 trang )

218
CHAPTER 7
■ THE LINQ TO XML API
Listing 7-34. Creating a Text Node and Passing It As the Value of a Created Element
XText xName = new XText("Joe");
XElement xFirstName = new XElement("FirstName", xName);
Console.WriteLine(xFirstName);
This code produces the exact same output as the previous example, and if we examine the internal
state of the xFirstName object, it too is identical to the one created in the previous example:
<FirstName>Joe</FirstName>
Creating CData with XCData
Creating an element with a CData value is also pretty simple. Listing 7-35 is an example.
Listing 7-35. Creating an XCData Node and Passing It As the Value of a Created Element
XElement xErrorMessage = new XElement("HTMLMessage",
new XCData("<H1>Invalid user id or password.</H1>"));
Console.WriteLine(xErrorMessage);
This code produces the following output:
<HTMLMessage><![CDATA[<H1>Invalid user id or password.</H1>]]></HTMLMessage>
As you can see, the LINQ to XML API makes handling CData simple.
XML Output
Of course, creating, modifying, and deleting XML data does no good if you cannot persist the changes.
This section contains a few ways to output your XML.
Saving with XDocument.Save()
You can save your XML document using any of several XDocument.Save methods. Here is a list of
prototypes:
void XDocument.Save(string filename);
void XDocument.Save(TextWriter textWriter);
void XDocument.Save(XmlWriter writer);
void XDocument.Save(string filename, SaveOptions options);
void XDocument.Save(TextWriter textWriter, SaveOptions options);
Listing 7-36 is an example where I save the XML document to a file in my project’s folder.


Rattz_789-3C07.fm Page 218 Tuesday, October 23, 2007 4:37 PM
CHAPTER 7 ■ THE LINQ TO XML API
219
Listing 7-36. Saving a Document with the XDocument.Save Method
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XAttribute("experience", "first-time"),
new XAttribute("language", "English"),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz"))));
xDocument.Save("bookparticipants.xml");
Notice that I called the Save method on an object of type XDocument. This is because the Save
methods are instance methods. The Load methods you will read about later in the “XML Input”
section are static methods and must be called on the XDocument or XElement class.
Here are the contents of the generated bookparticipants.xml file when viewing them in a text
editor such as Notepad:
<?xml version="1.0" encoding="utf-8"?>
<BookParticipants>
<BookParticipant type="Author" experience="first-time" language="English">
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
</BookParticipant>
</BookParticipants>
That XML document output is easy to read because the version of the Save method that I called
is formatting the output. That is, if I call the version of the Save method that accepts a string filename
and a SaveOptions argument, passing a value of SaveOptions.None would give the same results as the
previous. Had I called the Save method like this
xDocument.Save("bookparticipants.xml", SaveOptions.DisableFormatting);

the results in the file would look like this:
<?xml version="1.0" encoding="utf-8"?><BookParticipants><BookParticipant type=
"Author" experience="first-time" language="English"><FirstName>Joe</FirstName>
<LastName>Rattz</LastName></BookParticipant></BookParticipants>
This is one single continuous line of text. However, you would have to examine the file in a text
editor to see the difference because a browser will format it nicely for you.
Of course, you can use any of the other methods available to output your document as well; it’s
up to you.
Saving with XElement.Save()
I have said many times that with the LINQ to XML API, creating an XML document is not necessary.
And to save your XML to a file, it still isn’t. The XElement class has several Save methods for this purpose:
Rattz_789-3C07.fm Page 219 Tuesday, October 23, 2007 4:37 PM
220
CHAPTER 7
■ THE LINQ TO XML API
void XElement.Save(string filename);
void XElement.Save(TextWriter textWriter);
void XElement.Save(XmlWriter writer);
void XElement.Save(string filename, SaveOptions options);
void XElement.Save(TextWriter textWriter, SaveOptions options);
Listing 7-37 is an example very similar to the previous, except I never even create an XML
document.
Listing 7-37. Saving an Element with the XElement.Save Method
XElement bookParticipants =
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XAttribute("experience", "first-time"),
new XAttribute("language", "English"),
new XElement("FirstName", "Joe"),

new XElement("LastName", "Rattz")));
bookParticipants.Save("bookparticipants.xml");
And the saved XML looks identical to the previous example where I actually have an XML
document:
<?xml version="1.0" encoding="utf-8"?>
<BookParticipants>
<BookParticipant type="Author" experience="first-time" language="English">
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
</BookParticipant>
</BookParticipants>
XML Input
Creating and persisting XML to a file does no good if you can’t load it back into an XML tree. Here are
some techniques to read XML back in.
Loading with XDocument.Load()
Now that you know how to save your XML documents and fragments, you would probably like to
know how to load them. You can load your XML document using any of several methods. Here is a
list:
static XDocument XDocument.Load(string uri);
static XDocument XDocument.Load(TextReader textReader);
static XDocument XDocument.Load(XmlReader reader);
static XDocument XDocument.Load(string uri, LoadOptions options);
static XDocument XDocument.Load(TextReader textReader, LoadOptions options);
static XDocument XDocument.Load(XmlReader reader, LoadOptions options);
Rattz_789-3C07.fm Page 220 Tuesday, October 23, 2007 4:37 PM
CHAPTER 7 ■ THE LINQ TO XML API
221
You may notice how symmetrical these methods are to the XDocument.Save methods. However,
there are a couple differences worth pointing out. First, in the Save methods, you must call the Save
method on an object of XDocument or XElement type because the Save method is an instance method.

But the Load method is static, so you must call it on the XDocument class itself. Second, the Save methods
that accept a string are requiring filenames to be passed, whereas the Load methods that accept a
string are allowing a URI to be passed.
Additionally, the Load method allows a parameter of type LoadOptions to be specified while loading
the XML document. The LoadOptions enum has the options shown in Table 7-2.
These options can be combined with a bitwise OR (|) operation. However, some options will not
work in some contexts. For example, when creating an element or a document by parsing a string,
there is no line information available, nor is there a base URI. Or, when creating a document with an
XmlReader, there is no base URI.
Listing 7-38 shows an example where I load my XML document created in the previous example,
Listing 7-37.
Listing 7-38. Loading a Document with the XDocument.Load Method
XDocument xDocument = XDocument.Load("bookparticipants.xml",
LoadOptions.SetBaseUri | LoadOptions.SetLineInfo);
Console.WriteLine(xDocument);
XElement firstName = xDocument.Descendants("FirstName").First();
Console.WriteLine("FirstName Line:{0} - Position:{1}",
((IXmlLineInfo)firstName).LineNumber,
((IXmlLineInfo)firstName).LinePosition);
Console.WriteLine("FirstName Base URI:{0}", firstName.BaseUri);
■Note You must either add a using directive for System.Xml, if one is not present, or specify the namespace
when referencing the IXmlLineInfo interface in your code; otherwise, the IXmlLineInfo type will not be found.
Table 7-2. The LoadOptions Enumeration
Option Description
LoadOptions.None Use this option to specify that no load options are to
be used.
LoadOptions.PreserveWhitespace Use this option to preserve the whitespace in the XML
source, such as blank lines.
LoadOptions.SetLineInfo Use this option so that you may obtain the line and
position of any object inheriting from XObject by

using the IXmlLineInfo interface.
LoadOptions.SetBaseUri Use this option so that you may obtain the base URI of any
object inheriting from XObject.
Rattz_789-3C07.fm Page 221 Tuesday, October 23, 2007 4:37 PM
222
CHAPTER 7
■ THE LINQ TO XML API
This code is loading the same XML file I created in the previous example. After I load and display
the document, I obtain a reference for the FirstName element and display the line and position of the
element in the source XML document. Then I display the base URI for the element.
Here are the results:
<BookParticipants>
<BookParticipant type="Author" experience="first-time" language="English">
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
</BookParticipant>
</BookParticipants>
FirstName Line:4 - Position:6
FirstName Base URI:file:///C:/Documents and Settings/…/Projects/LINQChapter7/
LINQChapter7/bin/Debug/bookparticipants.xml
This output looks just as I would expect, with one possible exception. First, the actual XML
document looks fine. I see the line and position of the FirstName element, but the line number is
causing me concern. It is shown as four, but in the displayed XML document, the FirstName element
is on the third line. What is that about? If you examine the XML document I loaded, you will see that
it begins with the document declaration, which is omitted from the output:
<?xml version="1.0" encoding="utf-8"?>
This is why the FirstName element is being reported as being on line four.
Loading with XElement.Load()
Just as you could save from either an XDocument or XElement, we can load from either as well. Loading
into an element is virtually identical to loading into a document. Here are the methods available:

static XElement XElement.Load(string uri);
static XElement XElement.LoadTextReader textReader);
static XElement XElement.Load(XmlReader reader);
static XElement XElement.Load(string uri, LoadOptions options);
static XElement XElement.Load(TextReader textReader, LoadOptions options);
static XElement XElement.Load(XmlReader reader, LoadOptions options);
These methods are static just like the XDocument.Save methods, so they must be called from the
XElement class directly. Listing 7-39 contains an example loading the same XML file I saved with
the XElement.Save method in Listing 7-37.
Listing 7-39. Loading an Element with the XElement.Load Method
XElement xElement = XElement.Load("bookparticipants.xml");
Console.WriteLine(xElement);
Just as you already expect, the output looks like the following:
Rattz_789-3C07.fm Page 222 Tuesday, October 23, 2007 4:37 PM
CHAPTER 7 ■ THE LINQ TO XML API
223
<BookParticipants>
<BookParticipant type="Author" experience="first-time" language="English">
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
</BookParticipant>
</BookParticipants>
Just as the XDocument.Load method does, the XElement.Load method has overloads that accept a
LoadOptions parameter. Please see the description of these in the “Loading with XDocument.Load()”
section previously in the chapter.
Parsing with XDocument.Parse() or XElement.Parse()
How many times have you passed XML around in your programs as a string, only to suddenly need
to do some serious XML work? Getting the data from a string variable to an XML document type
variable always seems like such a hassle. Well, worry yourself no longer. One of my personal favorite
features of the LINQ to XML API is the parse method.

Both the XDocument and XElement classes have a static method named Parse for parsing XML strings.
I think by now you probably feel comfortable accepting that if you can parse with the XDocument class,
you can probably parse with the XElement class, and vice-versa. And since the LINQ to XML API is all
about the elements, baby, I am going to only give you an element example this time:
In the “Saving with XDocument.Save” section earlier in this chapter, I show the output of the
Save method if the LoadOptions parameter is specified as DisableFormatting. The result is a single
string of XML. For the example in Listing 7-40, I start with that XML string (after escaping the inner
quotes), parse it into an element, and output the XML element to the screen.
Listing 7-40. Parsing an XML String into an Element
string xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><BookParticipants>" +
"<BookParticipant type=\"Author\" experience=\"first-time\" language=" +
"\"English\"><FirstName>Joe</FirstName><LastName>Rattz</LastName>" +
"</BookParticipant></BookParticipants>";
XElement xElement = XElement.Parse(xml);
Console.WriteLine(xElement);
The results are the following:
<BookParticipants>
<BookParticipant type="Author" experience="first-time" language="English">
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
</BookParticipant>
</BookParticipants>
How cool is that? Remember the old days when you had to create a document using the W3C
XML DOM XmlDocument class? Thanks to the elimination of document centricity, you can turn XML
strings into real XML trees in the blink of an eye with one method call.
Rattz_789-3C07.fm Page 223 Tuesday, October 23, 2007 4:37 PM
224
CHAPTER 7
■ THE LINQ TO XML API
XML Traversal

XML traversal is primarily accomplished with 4 properties and 11 methods. In this section, I try to
mostly use the same code example for each property or method, except I change a single argument
on one line when possible. The example in Listing 7-41 builds a full XML document.
Listing 7-41. A Base Example Subsequent Examples May Be Derived From
// I will use this to store a reference to one of the elements in the XML tree.
XElement firstParticipant;
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
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"))));
Console.WriteLine(xDocument);
First, notice that I am saving a reference to the first BookParticipant element I construct. I do
this so that I can have a base element from which to do all the traversal. While I will not be using the
firstParticipant variable in this example, I will in the subsequent traversal examples. The next
thing to notice is the argument for the Console.WriteLine method. In this case, I output the docu-
ment itself. As I progress through these traversal examples, I change that argument to demonstrate
how to traverse the XML tree. So here is the output showing the document from the previous
example:
<!DOCTYPE BookParticipants SYSTEM "BookParticipants.dtd">

<?BookCataloger out-of-print?>
<BookParticipants>
<BookParticipant type="Author">
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
</BookParticipant>
<BookParticipant type="Editor">
<FirstName>Ewan</FirstName>
<LastName>Buckingham</LastName>
</BookParticipant>
</BookParticipants>
Rattz_789-3C07.fm Page 224 Tuesday, October 23, 2007 4:37 PM
CHAPTER 7 ■ THE LINQ TO XML API
225
Traversal Properties
I will begin my discussion with the primary traversal properties. When directions (up, down, etc.) are
specified, they are relative to the element the method is called on. In the subsequence examples, I
save a reference to the first BookParticipant element, and it is the base element used for the traversal.
Forward with XNode.NextNode
Traversing forward through the XML tree is accomplished with the NextNode property. Listing 7-42 is
an example.
Listing 7-42. Traversing Forward from an XElement Object via the NextNode Property
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.

new XElement("BookParticipants", firstParticipant =
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"))));
Console.WriteLine(firstParticipant.NextNode);
Since the base element is the first BookParticipant element, firstParticipant, traversing forward
should provide me with the second BookParticipant element. Here are the results:
<BookParticipant type="Editor">
<FirstName>Ewan</FirstName>
<LastName>Buckingham</LastName>
</BookParticipant>
Based on these results I would say I am right on the money. Would you believe me if I told you
that if I had accessed the PreviousNode property of the element it would have been null since it is the
first node in its parent’s node list? It’s true, but I’ll leave you the task of proving it to yourself.
Backward with XNode.PreviousNode
If you want to traverse the XML tree backward, use the PreviousNode property. Since there is no previous
node for the first participant node, I’ll get tricky and access the NextNode property first, obtaining the
second participant node, as I did in the previous example, from which I will obtain the PreviousNode.
If you got lost in that, I will end up back to the first participant node. That is, I will go forward with
NextNode to then go backward with PreviousNode, leaving me where I started. If you have ever heard
Rattz_789-3C07.fm Page 225 Tuesday, October 23, 2007 4:37 PM
226
CHAPTER 7
■ THE LINQ TO XML API
the expression “taking one step forward and taking two steps back,” with just one more access of the

PreviousNode property, you could actually do that. LINQ makes it possible. Listing 7-43 is the example.
Listing 7-43. Traversing Backward from an XElement Object via the PreviousNode Property
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
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"))));
Console.WriteLine(firstParticipant.NextNode.PreviousNode);
If this works as I expect, I should have the first BookParticipant element’s XML:
<BookParticipant type="Author">
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
</BookParticipant>
LINQ to XML actually makes traversing an XML tree fun.
Up to Document with XObject.Document
Obtaining the XML document from an XElement object is as simple as accessing the Document prop-
erty of the element. So please notice my change to the Console.WriteLine method call, shown in
Listing 7-44.

Listing 7-44. Accessing the XML Document from an XElement Object via the Document Property
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
new XElement("BookParticipant",
new XAttribute("type", "Author"),
Rattz_789-3C07.fm Page 226 Tuesday, October 23, 2007 4:37 PM
CHAPTER 7 ■ THE LINQ TO XML API
227
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
Console.WriteLine(firstParticipant.Document);
This will output the document, which is the same output as Listing 7-41, and here is the output
to prove it:
<!DOCTYPE BookParticipants SYSTEM "BookParticipants.dtd">
<?BookCataloger out-of-print?>
<BookParticipants>
<BookParticipant type="Author">
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>

</BookParticipant>
<BookParticipant type="Editor">
<FirstName>Ewan</FirstName>
<LastName>Buckingham</LastName>
</BookParticipant>
</BookParticipants>
Up with XObject.Parent
If you need to go up one level in the tree, it will probably be no surprise that the Parent property will
do the job. Changing the node passed to the WriteLine method to what’s shown in Listing 7-45 changes
the output (as you will see).
Listing 7-45. Traversing Up from an XElement Object via the Parent Property
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
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"))));
Console.WriteLine(firstParticipant.Parent);
Rattz_789-3C07.fm Page 227 Tuesday, October 23, 2007 4:37 PM

228
CHAPTER 7
■ THE LINQ TO XML API
The output is changed to this:
<BookParticipants>
<BookParticipant type="Author">
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
</BookParticipant>
<BookParticipant type="Editor">
<FirstName>Ewan</FirstName>
<LastName>Buckingham</LastName>
</BookParticipant>
</BookParticipants>
Don’t let that fool you either. This is not the entire document. Notice it is missing the document
type and processing instruction.
Traversal Methods
To demonstrate the traversal methods, since they return sequences of multiple nodes, I must now
change that single Console.WriteLine method call to a foreach loop to output the potential multiple
nodes. This will result in the former call to the Console.WriteLine method looking basically like this:
foreach(XNode node in firstParticipant.Nodes())
{
Console.WriteLine(node);
}
From example to example, the only thing changing will be the method called on the
firstParticipant node in the foreach statement.
Down with XContainer.Nodes()
No, I am not expressing my disdain for nodes. Nor am I stating I am all in favor of nodes, as in being
“down for” rock climbing, meaning being excited about the prospect of going rock climbing. I am
merely describing the direction of traversal I am about to discuss.

Traversing down an XML tree is easily accomplished with a call to the Nodes method. It will
return a sequence of an object’s child XNode objects. In case you snoozed through some of the earlier
chapters, a sequence is an IEnumerable<T>, meaning an IEnumerable of some type T. Listing 7-46 is
the example.
Listing 7-46. Traversing Down from an XElement Object via the Nodes Method
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
new XElement("BookParticipant",
new XAttribute("type", "Author"),
Rattz_789-3C07.fm Page 228 Tuesday, October 23, 2007 4:37 PM
CHAPTER 7 ■ THE LINQ TO XML API
229
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
foreach (XNode node in firstParticipant.Nodes())
{
Console.WriteLine(node);
}
Here is the output:

<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
Don’t forget, that method is returning all child nodes, not just elements. So any other nodes in
the first participant’s list of child nodes will be included. This could include comments (XComment),
text (XText), processing instructions (XProcessingInstruction), document type (XDocumentType), or
elements (XElement). Also notice that it does not include the attribute because an attribute is not a node.
To provide a better example of the Nodes method, let’s look at the code in Listing 7-47. It is similar to
the base example with some extra nodes thrown in.
Listing 7-47. Traversing Down from an XElement Object via the Nodes Method with Additional
Node Types
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
new XElement("BookParticipant",
new XComment("This is a new author."),
new XProcessingInstruction("AuthorHandler", "new"),
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"))));
foreach (XNode node in firstParticipant.Nodes())

{
Console.WriteLine(node);
}
Rattz_789-3C07.fm Page 229 Tuesday, October 23, 2007 4:37 PM
230
CHAPTER 7
■ THE LINQ TO XML API
This example is different than the previous in that there is now a comment and processing
instruction added to the first BookParticipant element. Pressing Ctrl+F5 displays the following:
<! This is a new author >
<?AuthorHandler new?>
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
We can now see the comment and the processing instruction. What if you only want a certain
type of node, though, such as just the elements? Do you recall from Chapter 4 the OfType operator? I
can use that operator to return only the nodes that are of a specific type, such as XElement. Using the
same basic code as Listing 7-47, to return just the elements, I will merely change the foreach line, as
shown in Listing 7-48.
Listing 7-48. Using the OfType Operator to Return Just the Elements
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
new XElement("BookParticipant",
new XComment("This is a new author."),

new XProcessingInstruction("AuthorHandler", "new"),
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"))));
foreach (XNode node in firstParticipant.Nodes().OfType<XElement>())
{
Console.WriteLine(node);
}
As you can see, the XComment and XProcessingInstruction objects are still being created. But
since I am now calling the OfType operator, the code produces these results:
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
Are you starting to see how cleverly all the new C# language features and LINQ are coming together?
Isn’t it cool that we can use that Standard Query Operator to restrict the sequence of XML nodes this
way? So if you want to get just the comments from the first BookParticipant element, could you use
the OfType operator to do so? Of course you could, and the code would look like Listing 7-49.
Rattz_789-3C07.fm Page 230 Tuesday, October 23, 2007 4:37 PM
CHAPTER 7 ■ THE LINQ TO XML API
231
Listing 7-49. Using the OfType Operator to Return Just the Comments
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),

// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
new XElement("BookParticipant",
new XComment("This is a new author."),
new XProcessingInstruction("AuthorHandler", "new"),
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"))));
foreach (XNode node in firstParticipant.Nodes().OfType<XComment>())
{
Console.WriteLine(node);
}
Here is the output:
<! This is a new author >
Just to be anticlimactic, can you use the OfType operator to get just the attributes? No, you
cannot. This is a trick question. Remember that unlike the W3C XML DOM API, with the LINQ to
XML API, attributes are not nodes in the XML tree. They are a sequence of name-value pairs hanging
off the element. To get to the attributes of the first BookParticipant node, I would change the code
to that in Listing 7-50.
Listing 7-50. Accessing an Element’s Attributes Using the Attributes Method
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),

new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
new XElement("BookParticipant",
new XComment("This is a new author."),
new XProcessingInstruction("AuthorHandler", "new"),
Rattz_789-3C07.fm Page 231 Tuesday, October 23, 2007 4:37 PM
232
CHAPTER 7
■ THE LINQ TO XML API
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"))));
foreach (XAttribute attr in firstParticipant.Attributes())
{
Console.WriteLine(attr);
}
Notice I had to change more than just the property or method of the first BookParticipant
element that I was accessing. I also had to change the enumeration variable type to XAttribute,
because XAttribute doesn’t inherit from XNode. Here are the results:
type="Author"
Down with XContainer.Elements()
Because the LINQ to XML API is so focused on elements, and that is what we are working with most,
Microsoft provides a quick way to get just the elements of an element’s child nodes using the Elements
method. It is the equivalent of calling the OfType<XElement> method on the sequence returned by the

Nodes method.
Listing 7-51 is an example that is logically the same as Listing 7-48.
Listing 7-51. Accessing an Element’s Child Elements Using the Elements Method
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
new XElement("BookParticipant",
new XComment("This is a new author."),
new XProcessingInstruction("AuthorHandler", "new"),
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"))));
Rattz_789-3C07.fm Page 232 Tuesday, October 23, 2007 4:37 PM
CHAPTER 7 ■ THE LINQ TO XML API
233
foreach (XNode node in firstParticipant.Elements())
{
Console.WriteLine(node);
}
This code produces the exact same results as Listing 7-48:

<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
The Elements method also has an overloaded version that allows you to pass the name of the
element you are looking for, as in Listing 7-52.
Listing 7-52. Accessing Named Child Elements Using the Elements Method
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
new XElement("BookParticipant",
new XComment("This is a new author."),
new XProcessingInstruction("AuthorHandler", "new"),
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"))));
foreach (XNode node in firstParticipant.Elements("FirstName"))
{
Console.WriteLine(node);
}
This code produces the following:
<FirstName>Joe</FirstName>

Down with XContainer.Element()
You may obtain the first child element matching a specified name using the Element method. Instead
of a sequence being returned requiring a foreach loop, I will have a single element returned, as shown
in Listing 7-53.
Rattz_789-3C07.fm Page 233 Tuesday, October 23, 2007 4:37 PM
234
CHAPTER 7
■ THE LINQ TO XML API
Listing 7-53. Accessing the First Child Element with a Specified Name
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
new XElement("BookParticipant",
new XComment("This is a new author."),
new XProcessingInstruction("AuthorHandler", "new"),
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"))));
Console.WriteLine(firstParticipant.Element("FirstName"));
This code outputs the following:

<FirstName>Joe</FirstName>
Up Recursively with XNode.Ancestors()
While you can obtain the single parent element using a node’s Parent property, you can get a sequence
of the ancestor elements using the Ancestors method. This is different in that it recursively traverses
up the XML tree instead of stopping one level up, and it only returns elements, as opposed to nodes.
To make this demonstration more clear, I will add some child nodes to the first book participant’s
FirstName element. Also, instead of enumerating through the ancestors of the first BookParticipant
element, I use the Element method to reach down two levels to the newly added NickName element.
This provides more ancestors to provide greater clarity. The code is shown in Listing 7-54.
Listing 7-54. Traversing Up from an XElement Object via the Ancestors Method
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
new XElement("BookParticipant",
new XComment("This is a new author."),
Rattz_789-3C07.fm Page 234 Tuesday, October 23, 2007 4:37 PM
CHAPTER 7 ■ THE LINQ TO XML API
235
new XProcessingInstruction("AuthorHandler", "new"),
new XAttribute("type", "Author"),
new XElement("FirstName",
new XText("Joe"),
new XElement("NickName", "Joey")),
new XElement("LastName", "Rattz")),

new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
foreach (XElement element in firstParticipant.
Element("FirstName").Element("NickName").Ancestors())
{
Console.WriteLine(element.Name);
}
Again, please notice I add some child nodes to the first book participant’s FirstName element.
This causes the first book participant’s FirstName element to have contents that include an XText
object equal to the string "Joe", and to have a child element, NickName. I retrieve the first book partic-
ipant’s FirstName element’s NickName element for which to retrieve the ancestors. In addition, notice
I used an XElement type variable instead of an XNode type for enumerating through the sequence
returned from the Ancestors method. This is so I can access the Name property of the element. Instead
of displaying the element’s XML as I have done in past examples, I am only displaying the name of
each element in the ancestor’s sequence. I do this because it would be confusing to display each
ancestor’s XML, because each would include the previous and it would get very recursive, thereby
obscuring the results. That all said, here they are:
FirstName
BookParticipant
BookParticipants
Just as expected, the code recursively traverses up the XML tree.
Up Recursively with XElement.AncestorsAndSelf()
This method works just like the Ancestors method, except it includes itself in the returned sequence
of ancestors. Listing 7-55 is the same example as before, except it calls the AncestorsAndSelf method.
Listing 7-55. Traversing Up from an XElement Object via the AncestorsAndSelf Method
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(

new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
new XElement("BookParticipant",
new XComment("This is a new author."),
Rattz_789-3C07.fm Page 235 Tuesday, October 23, 2007 4:37 PM
236
CHAPTER 7
■ THE LINQ TO XML API
new XProcessingInstruction("AuthorHandler", "new"),
new XAttribute("type", "Author"),
new XElement("FirstName",
new XText("Joe"),
new XElement("NickName", "Joey")),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
foreach (XElement element in firstParticipant.
Element("FirstName").Element("NickName").AncestorsAndSelf())
{
Console.WriteLine(element.Name);
}
The results should be the same as when calling the Ancestors method, except I should also see
the NickName element’s name at the beginning of the output:
NickName

FirstName
BookParticipant
BookParticipants
Down Recursively with XContainer.Descendants()
In addition to recursively traversing up, you can recursively traverse down with the Descendants method.
Again, this method only returns elements. There is an equivalent method named DescendantNodes that
will return all descendant nodes. Listing 7-56 is the same code as the previous, except I call the
Descendants method on the first book participant element.
Listing 7-56. Traversing Down from an XElement Object via the Descendants Method
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
new XElement("BookParticipant",
new XComment("This is a new author."),
new XProcessingInstruction("AuthorHandler", "new"),
new XAttribute("type", "Author"),
new XElement("FirstName",
new XText("Joe"),
new XElement("NickName", "Joey")),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
Rattz_789-3C07.fm Page 236 Tuesday, October 23, 2007 4:37 PM
CHAPTER 7 ■ THE LINQ TO XML API
237

new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
foreach (XElement element in firstParticipant.Descendants())
{
Console.WriteLine(element.Name);
}
The results are the following:
FirstName
NickName
LastName
As you can see, it traverses all the way to the end of every branch in the XML tree.
Down Recursively with XElement.DescendantsAndSelf()
Just as the Ancestors method has an AncestorsAndSelf method variation, so too does the Descendants
method. The DescendantsAndSelf method works just like the Descendants method, except it also
includes the element itself in the returned sequence. Listing 7-57 is the same example that I used for
the Descendants method call, with the exception that now it calls the DescendantsAndSelf method.
Listing 7-57. Traversing Down from an XElement Object via the DescendantsAndSelf Method
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants", firstParticipant =
new XElement("BookParticipant",
new XComment("This is a new author."),
new XProcessingInstruction("AuthorHandler", "new"),

new XAttribute("type", "Author"),
new XElement("FirstName",
new XText("Joe"),
new XElement("NickName", "Joey")),
new XElement("LastName", "Rattz")),
new XElement("BookParticipant",
new XAttribute("type", "Editor"),
new XElement("FirstName", "Ewan"),
new XElement("LastName", "Buckingham"))));
foreach (XElement element in firstParticipant.DescendantsAndSelf())
{
Console.WriteLine(element.Name);
}
Rattz_789-3C07.fm Page 237 Tuesday, October 23, 2007 4:37 PM
238
CHAPTER 7
■ THE LINQ TO XML API
So does the output also include the firstParticipant element’s name?
BookParticipant
FirstName
NickName
LastName
Of course it does.
Forward with XNode.NodesAfterSelf()
For this example, in addition to changing the foreach call, I add a couple of comments to the
BookParticipants element to make the distinction between retrieving nodes and elements more evident,
since XComment is a node but not an element. Listing 7-58 is what the code looks like for this example.
Listing 7-58. Traversing Forward from the Current Node Using the NodesAfterSelf Method
XElement firstParticipant;
// A full document with all the bells and whistles.

XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants",
new XComment("Begin Of List"), firstParticipant =
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")),
new XComment("End Of List")));
foreach (XNode node in firstParticipant.NodesAfterSelf())
{
Console.WriteLine(node);
}
Notice that I added two comments that are siblings of the two BookParticipant elements. This
modification to the constructed XML document will be made for the NodesAfterSelf, ElementsAfterSelf,
NodesBeforeSelf, and ElementsBeforeSelf examples.
This causes all sibling nodes after the first BookParticipant node to be enumerated. Here are
the results:
Rattz_789-3C07.fm Page 238 Tuesday, October 23, 2007 4:37 PM
CHAPTER 7 ■ THE LINQ TO XML API
239
<BookParticipant type="Editor">

<FirstName>Ewan</FirstName>
<LastName>Buckingham</LastName>
</BookParticipant>
<! End Of List >
As you can see, the last comment is included in the output because it is a node. Don’t let that output
fool you. The NodesAfterSelf method only returns two nodes: the BookParticipant element whose
type attribute is Editor and the End Of List comment. Those other nodes, FirstName and LastName
are merely displayed because the ToString method is being called on the BookParticipant node.
Keep in mind that this method returns nodes, not just elements. If you want to limit the type of
nodes returned, you could use the TypeOf operator as I have demonstrated in previous examples. But
if the type you are interested in is elements, there is a method just for that called ElementsAfterSelf.
Forward with XNode.ElementsAfterSelf()
This example uses the same modifications to the XML document made in Listing 7-58 concerning
the addition of two comments.
To get a sequence of just the sibling elements after the referenced node, you call the
ElementsAfterSelf method, as shown in Listing 7-59.
Listing 7-59. Traversing Forward from the Current Node Using the ElementsAfterSelf Method
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants",
new XComment("Begin Of List"), firstParticipant =
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")),
new XComment("End Of List")));
foreach (XNode node in firstParticipant.ElementsAfterSelf())
{
Console.WriteLine(node);
}
Rattz_789-3C07.fm Page 239 Tuesday, October 23, 2007 4:37 PM
240
CHAPTER 7
■ THE LINQ TO XML API
The example code with these modifications produces the following results:
<BookParticipant type="Editor">
<FirstName>Ewan</FirstName>
<LastName>Buckingham</LastName>
</BookParticipant>
Notice that the comment is excluded this time because it is not an element. Again, the FirstName
and LastName elements are only displayed because they are the content of the BookParticipant element
that was retrieved and because the ToString method was called on the element.
Backward with XNode.NodesBeforeSelf()
This example uses the same modifications to the XML document made in Listing 7-58 concerning
the addition of two comments.
This method works just like NodesAfterSelf except it retrieves the sibling nodes before the
referenced node. In the example code, since the initial reference into the document is the first
BookParticipant node, I obtain a reference to the second BookParticipant node using the NextNode
property of the first BookParticipant node so that there are more nodes to return, as shown in
Listing 7-60.

Listing 7-60. Traversing Backward from the Current Node
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants",
new XComment("Begin Of List"), firstParticipant =
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")),
new XComment("End Of List")));
foreach (XNode node in firstParticipant.NextNode.NodesBeforeSelf())
{
Console.WriteLine(node);
}
This modification should result in the return of the first BookParticipant node and the first
comment. Here are the results:
Rattz_789-3C07.fm Page 240 Tuesday, October 23, 2007 4:37 PM
CHAPTER 7 ■ THE LINQ TO XML API
241
<! Begin Of List >

<BookParticipant type="Author">
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
</BookParticipant>
Interesting! I was expecting the two nodes that were returned, the comment and the first
BookParticipant, to be in the reverse order. I expected the method to start with the referenced node
and build a sequence via the PreviousNode property. Perhaps it did indeed do this but then called the
Reverse or InDocumentOrder operator. I cover the InDocumentOrder operator in the next chapter. Again,
don’t let the FirstName and LastName nodes confuse you. The NodesBeforeSelf method did not return
those. It is only because the ToString method was called on the first BookParticipant node, by the
Console.WriteLine method, that they are displayed.
Backward with XNode.ElementsBeforeSelf()
This example uses the same modifications to the XML document made in Listing 7-58 concerning
the addition of two comments.
Just like the NodesAfterSelf method has a companion method named ElementsAfterSelf to
return only the elements, so too does the NodesBeforeSelf method. The ElementsBeforeSelf method
returns only the sibling elements before the referenced node, as shown in Listing 7-61.
Listing 7-61. Traversing Backward from the Current Node
XElement firstParticipant;
// A full document with all the bells and whistles.
XDocument xDocument = new XDocument(
new XDeclaration("1.0", "UTF-8", "yes"),
new XDocumentType("BookParticipants", null, "BookParticipants.dtd", null),
new XProcessingInstruction("BookCataloger", "out-of-print"),
// Notice on the next line that I am saving off a reference to the first
// BookParticipant element.
new XElement("BookParticipants",
new XComment("Begin Of List"), firstParticipant =
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")),
new XComment("End Of List")));
foreach (XNode node in firstParticipant.NextNode.ElementsBeforeSelf())
{
Console.WriteLine(node);
}
Notice that again I obtain a reference to the second BookParticipant node via the NextNode
property. Will the output contain the comment?
Rattz_789-3C07.fm Page 241 Tuesday, October 23, 2007 4:37 PM
242
CHAPTER 7
■ THE LINQ TO XML API
<BookParticipant type="Author">
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
</BookParticipant>
Of course not, because it is not an element.
XML Modification
Modifying XML data is easier than ever with the LINQ to XML API. With just a handful of methods,
you can perform all the modifications you could want. Whether it is adding, changing, or deleting
nodes or elements, there is a method to get the job done.
As has been stated time and time again, with the LINQ to XML API, you will be working with
XElement objects most of the time. Because of this, the majority of these examples are with elements.
The LINQ to XML API classes inheriting from XNode are covered first, followed by a section on attributes.
Adding Nodes

In this section on adding nodes to an XML tree, I start with a base example of the code in Listing 7-62.
Listing 7-62. A Base Example with a Single Book Participant
// A document with one book participant.
XDocument xDocument = new XDocument(
new XElement("BookParticipants",
new XElement("BookParticipant",
new XAttribute("type", "Author"),
new XElement("FirstName", "Joe"),
new XElement("LastName", "Rattz"))));
Console.WriteLine(xDocument);
This code produces an XML tree with a single book participant. Here is the code’s output:
<BookParticipants>
<BookParticipant type="Author">
<FirstName>Joe</FirstName>
<LastName>Rattz</LastName>
</BookParticipant>
</BookParticipants>
For the different methods to add nodes, I will start with this basic code.
■Note While the following examples all add elements, the techniques used to add the elements work for all LINQ
to XML classes that inherit from the XNode class.
Rattz_789-3C07.fm Page 242 Tuesday, October 23, 2007 4:37 PM

×