Selecting Nodes: Axis, Node Test, Predicate, and Function. You Might Be Wondering Where They Come Into The Pic
Selecting Nodes: Axis, Node Test, Predicate, and Function. You Might Be Wondering Where They Come Into The Pic
Selecting Nodes
This chapter began with a brief overview of XPath and its vocabulary, including terms such as
axis, node test, predicate, and function. You might be wondering where they come into the pic-
ture. It’s time now to see those features in action.
To test various XPath expressions, we will create a simple application that looks like the
one shown in Figure 4-3.
The application consists of a text box positioned at the top to enter XPath expressions.
After you click the Execute button, the given expression is executed and its results are displayed
in another text box at the bottom. The label at the bottom displays the total number of rows
returned by the expression. Listing 4-3 shows the Click event handler of the Execute button.
CHAPTER 4 ■ AC CES SING XML DOCUM ENTS BY USING THE XPATH DA TA MODEL 101
{
textBox2.Text = iterator.Current.OuterXml;
}
}
else
{
textBox2.Text = "No results";
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
The code creates an instance of XPathDocument by passing the path of the Employees.xml file.
Then an XPathNavigator is obtained by using the CreateNavigator() method of XPathDocument.
The code then calls the Select() method of XPathNavigator, which accepts an XPath expression
and returns an instance of XPathNodeIterator.
The XPathNodeIterator class allows you to iterate through the returned nodes and has a
number of properties and methods to assist you. To start with, the Count property tells you how
many nodes were selected by the Select() method. After you are satisfied that there were some
results, you can iterate through the selected nodes by using the MoveNext() method. On each
node, you then use the Current property to give you a reference to the XPathNavigator that
is positioned at the current node. You can then call any of the methods and properties of
XPathNavigator.
In our example, we simply display the OuterXml property of the underlying XPathNavigator in
a text box. Though not used in our example, the CurrentPosition property of XPathNodeIterator
returns the current index of the node being accessed.
Now let’s try some XPath expressions by using our application. Some XPath expressions
relevant to our XML document (Employees.xml) are given in Table 4-6.
Table 4-6. Examples of XPath Expressions Try to execute these XPath Expressions.
Purpose Expression
To select an employee whose employee employees/employee[@employeeid=1]
ID is 1
To select the employee whose first name employees/employee[firstname/text()='Andrew']
is Andrew
To select the last employee from the employees/employee[last()]
document
To select the employee whose index is 2 employees/employee[position()=2]
To select an employee whose name employees/employee[contains(firstname,'Nancy')]
contains Nancy
To select the name of the first employee employees/employee/firstname[text()]
Joshi_09973C04.fm Page 110 Monday, June 9, 2008 9:13 AM
The application consists of two text boxes: one to accept the employee ID to be extracted,
and the other to specify a file path where the extracted employee details are stored.
Listing 4-10 shows the Click event handler of the Write button.
while (navigator.MoveToNext())
{
navigator.MoveToFirstChild();
do
{
string id = navigator.GetAttribute("employeeid", "");
if (id == textBox1.Text)
Joshi_09973C04.fm Page 111 Monday, June 9, 2008 9:13 AM
CHAPTER 4 ■ AC CES SING XML DOCUM ENTS BY USING THE XPATH DA TA MODEL 111
{
XmlTextWriter writer = new XmlTextWriter(textBox2.Text, null);
navigator.WriteSubtree(writer);
writer.Close();
if (MessageBox.Show("Do you want to see the file?",
"Question",
MessageBoxButtons.YesNo) == DialogResult.Yes)
{
System.Diagnostics.Process.Start(textBox2.Text);
}
}
}
while (navigator.MoveToNext());
}
}
If the user clicks Yes, the resultant XML file is opened in the browser. Note the use of the
Process class from the System.Diagnostics namespace. The Start() method of this class
accepts a filename and opens it in its associated application. Figure 4-11 shows a sample out-
put document with the <employee> subtree extracted.
Joshi_09973C04.fm Page 112 Monday, June 9, 2008 9:13 AM
The application consists of text boxes to supply values for employee ID, first name, last
name, home phone, and notes. There are four buttons for adding a new employee, modifying
an existing employee, deleting an existing employee, and saving the changed document,
Joshi_09973C04.fm Page 113 Monday, June 9, 2008 9:13 AM
CHAPTER 4 ■ AC CES SING XML DOCUM ENTS BY USING THE XPATH DA TA MODEL 113
respectively. When you enter an employee ID and click the Show button, the details of that
employee are displayed in the remaining text boxes. You can change the details as per your
requirements and click the Add, Update, or Delete buttons to add, update, or delete an
employee, respectively. To save the modified document, you need to click the Save button.
In the source code of the application, you will find two form variables declared as shown
Listing 4-11.
The Employees.xml file is loaded into this XmlDocument, and an XPathNavigator is obtained
from it. This code goes in the Load event of the form and is shown in Listing 4-12.
When the user enters an employee ID and clicks the Show button, the details of that
employee need to be displayed in the remaining text boxes. The Click event handler of the
Show button does this job, as shown in Listing 4-13.
while (navigator.MoveToNext())
{
navigator.MoveToFirstChild();
do
{
string id = navigator.GetAttribute("employeeid", "");
if (id == textBox1.Text)
{
navigator.MoveToFirstChild();
Joshi_09973C04.fm Page 114 Monday, June 9, 2008 9:13 AM
do
{
switch (navigator.Name)
{
case "firstname":
textBox2.Text = navigator.Value;
break;
case "lastname":
textBox3.Text = navigator.Value;
break;
case "homephone":
textBox4.Text = navigator.Value;
break;
case "notes":
textBox5.Text = navigator.Value;
break;
}
}
while (navigator.MoveToNext());
navigator.MoveToParent();
}
}
while (navigator.MoveToNext());
}
}
The code should be familiar to you, because we used something similar in previous exam-
ples. The code loops through all the <employee> nodes and finds the one that matches the
supplied employee ID. The values of various child nodes such as <firstname>, <lastname>,
<homephone>, and <notes> are displayed in the respective text boxes by using the Value property
of XPathNavigator.
Adding Nodes
To add new nodes to the document, the XPathNavigator class provides a method called
AppendChild(). The AppendChild() method returns an instance of XmlWriter, and by using
this XmlWriter you can write additional nodes to the document. The newly written nodes are
added as child nodes of the current node. Listing 4-14 shows how this is accomplished.
Joshi_09973C04.fm Page 115 Monday, June 9, 2008 9:13 AM
CHAPTER 4 ■ AC CES SING XML DOCUM ENTS BY USING THE XPATH DA TA MODEL 115
while (navigator.MoveToNext())
{
XmlWriter writer = navigator.AppendChild();
writer.WriteStartElement("employee");
writer.WriteAttributeString("employeeid", textBox1.Text);
writer.WriteElementString("firstname", textBox2.Text);
writer.WriteElementString("lastname", textBox3.Text);
writer.WriteElementString("homephone", textBox4.Text);
writer.WriteElementString("notes", textBox5.Text);
writer.WriteEndElement();
writer.Close();
}
}
The code first navigates to the <employees> node. This is where we want to add a new
<employee> child node. Then it calls the AppendChild() method of the XPathNavigator. The
returned XmlWriter is used to add a new <employee> node with an employeeid attribute.
The child nodes of the <employee> node (<firstname>, <lastname>, <homephone>, and <notes>)
are also added. The methods such as WriteStartElement()and WriteEndElement() should
already be familiar to you from Chapter 3.
■Note There are a few other overloads of the AppendChild() method. For example, one overloaded
method accepts the complete XML markup fragment for the new node and appends it to the current node.
However, the one that we used is more flexible.
Modifying Nodes
To modify contents of any of the nodes, the XPathNavigator class provides a method called
SetValue(), which accepts the new value and assigns it to the current node. Listing 4-15 shows
how this method can be used.
Joshi_09973C04.fm Page 116 Monday, June 9, 2008 9:13 AM
while (navigator.MoveToNext())
{
navigator.MoveToFirstChild();
do
{
string id = navigator.GetAttribute("employeeid", "");
if (id == textBox1.Text)
{
navigator.MoveToFirstChild();
do
{
switch (navigator.Name)
{
case "firstname":
navigator.SetValue(textBox2.Text);
break;
case "lastname":
navigator.SetValue(textBox3.Text);
break;
case "homephone":
navigator.SetValue(textBox4.Text);
break;
case "notes":
navigator.SetValue(textBox5.Text);
break;
}
}
while (navigator.MoveToNext());
navigator.MoveToParent();
}
}
while (navigator.MoveToNext());
}
}
Joshi_09973C04.fm Page 117 Monday, June 9, 2008 9:13 AM
CHAPTER 4 ■ AC CES SING XML DOCUM ENTS BY USING THE XPATH DA TA MODEL 117
As before, the code finds out the <employee> node that is to be updated. The switch state-
ment checks the Name property of XPathNavigator for the required node names (firstname,
lastname, homephone, and notes). Inside each case, the SetValue() method is called on the nav-
igator by passing the new value from the appropriate text box.
Deleting Nodes
Deleting a node is fairly simple. The DeleteSelf() method of XPathNavigator deletes the cur-
rent node. After the node is successfully deleted, the cursor is moved to the parent node of the
deleted node. Listing 4-16 shows the usage of DeleteSelf().
while (navigator.MoveToNext())
{
navigator.MoveToFirstChild();
do
{
string id = navigator.GetAttribute("employeeid", "");
if (id == textBox1.Text)
{
navigator.DeleteSelf();
}
}
while (navigator.MoveToNext());
}
}
As in the previous case, the code looks for a specific <employee> node. After it finds the
node, it calls the DeleteSelf() method on the navigator.
Saving Changes
It is important to remember that while making any modifications via XPathNavigator, the
changes are not saved automatically to disk. The changes affect only the DOM tree loaded in
memory, so you need to save the underlying document by calling the Save() method of the
XmlDocument class. This is illustrated in Listing 4-17.
Joshi_09973C04.fm Page 118 Monday, June 9, 2008 9:13 AM
Summary
In this chapter, you learned what XPath is and how to use XPath expressions in the .NET
Framework. We covered in detail the XPathNavigator class, which represents the XPath data
model of the .NET Framework. The XPathNavigator class can be constructed from either of the
XPathDocument or XmlDocument classes. The XPathNavigator returned from XPathDocument is
read-only, whereas that returned from XmlDocument is editable. You also learned how to select
nodes from the XML document by using XPath expressions in string form as well as in com-
piled form.
Joshi_09973C06.fm Page 172 Wednesday, June 4, 2008 8:28 AM
• A class inheriting from the TextWriter abstract class (for example, StringWriter or
StreamWriter)
As shown in Figure 6-5, the application consists of three text boxes to accept the source
XML filename, the XSLT style sheet filename, and the destination filename, respectively. Clicking
the Transform button performs the transformation, and the output of the transformation is
stored in a file specified by the Destination File text box. You can also open the destination file
after a successful transformation by selecting the check box. Listing 6-9 shows the Click event
handler of the Transform button.
<html>
<body>
<h1>Employee Listing</h1>
<table border="1">
<tr>
<th>Employee ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Home Phone</th>
<th>Notes</th>
</tr>
<tr>
<td>1</td>
<td>Nancy</td>
<td>Davolio</td>
<td>(206) 555-9857</td>
<td>
includes a BA in psychology from Colorado State University in 1970.
She also completed "The Art of the Cold Call." Nancy is a member
of Toastmasters International.
</td>
</tr>
<tr>
<td>2</td>
<td>Andrew</td>
<td>Fuller</td>
<td>(206) 555-9482</td>
Joshi_09973C06.fm Page 174 Wednesday, June 4, 2008 8:28 AM
<td>
Andrew received his BTS commercial in 1974 and a Ph.D. in international
marketing from the University of Dallas in 1981. He is fluent in French
and Italian and reads German. He joined the company as a sales
representative, was promoted to sales manager in January 1992 and to vice
president of sales in March 1993. Andrew is a member of the Sales
Management Roundtable, the Seattle Chamber of Commerce, and the Pacific
Rim Importers Association.
</td>
</tr>
<tr>
<td>3</td>
<td>Janet</td>
<td>Leverling</td>
<td>(206) 555-3412</td>
<td>
Janet has a BS degree in chemistry from Boston College (1984).
She has also completed a certificate program in food retailing management.
Janet was hired as a sales associate in 1991 and promoted to sales
representative in February 1992.
</td>
</tr>
</table>
</body>
</html>
As you can see, the source XML markup is transformed into HTML markup as specified in
the style sheet.
■Note In our example, we converted XML markup into HTML markup. However, you can easily use the
XslCompiledTransform class to transform source XML into another XML representation.
Figure 6-6. Application for passing parameters to the XSLT style sheet
The application consists of a single text box for accepting the first name of the employee.
Clicking the Transform button applies the style sheet and stores the resultant output in an
HTML file. Our XML file remains the same (Employees.xml). However, you need to modify the
style sheet from Listing 6-5 as shown in Listing 6-11.
<td>
<xsl:value-of select="notes"/>
</td>
</tr>
</xsl:if>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>
Notice the style sheet markup displayed in bold. At the top of the style sheet, we have declared
a parameter by using the <xsl:param> element. The name attribute of the <xsl:param> element
indicates the name of the parameter (firstname in our example). To use this parameter further
in the XSLT, you prefix it with the dollar symbol ($). Notice the firstname parameter of the <xsl:if>
element. Listing 6-12 shows the code that passes this parameter value at the time of actual
transformation.
The code declares three string variables to store the paths of the source XML file, the XSLT
style sheet file, and the destination HTML file, respectively. Then the code creates a FileStream
object for writing to the destination HTML file. This FileStream object will be passed to the
Transform() method later.
A new instance of the XslCompiledTransform class is then created, and the Load() method
loads the XSLT style sheet. Then comes the important part. The code creates an instance of the
XsltArgumentList class and adds a parameter to it by using its AddParam() method, which takes
Joshi_09973C06.fm Page 177 Wednesday, June 4, 2008 8:28 AM
three parameters: the name of the parameter, the namespace if any, and the parameter value.
Then, the Transform() method of XslCompiledTransform is called by passing the XsltArgumentList
object that we just created. This time, we pass the source filename, the parameter list, and a
stream to which the resultant output will be written. In our case, this stream points to the
Employees.html file. After the transformation is over, the stream is closed, and the newly gener-
ated HTML file is shown to the user.
If you run the application and supply Nancy as the parameter value, the resultant HTML
file will look like Listing 6-13.
<html>
<body>
<h1>Employee Listing</h1>
<table border="1">
<tr>
<th>Employee ID</th>
<th>First Name</th>
<th>Last Name</th>
<th>Home Phone</th>
<th>Notes</th>
</tr>
<tr>
<td>1</td>
<td>Nancy</td>
<td>Davolio</td>
<td>(206) 555-9857</td>
<td>
includes a BA in psychology from Colorado State University in 1970.
She also completed "The Art of the Cold Call." Nancy is a member of
Toastmasters International.
</td>
</tr>
</table>
</body>
</html>
As you can see, only one record is transformed, indicating that the output was indeed
filtered based on the parameter value.