Professional Documents
Culture Documents
INFO-3138 Tutorial 9 - XPath in C Sharp
INFO-3138 Tutorial 9 - XPath in C Sharp
Tutorial 9: XPath in C#
Purpose
In previous lessons we’ve examined XPath in isolation to be able to focus on the syntax of this
tool without getting distracted by context. As discussed earlier however, XPath isn’t particularly
useful on its own. In this tutorial we’ll look at integrating XPath into a simple C# console
program that selects different sets of food elements from Nutrition.xml.
Note that doc is references a populated instance of XmlDocument (the DOM). We’re using the
SelectNodes() method of this class to run the XPath expression “//daily-values/*”. This returns
an XmlNodeList collection containing all matching nodes. These are the child elements of the
daily-values element. Two other member variables of the Program class, _parameters and
_parameterUnits, are used to store the names of the elements (the nutritional substances) and
the unit of measurement used for each substance. Notice that in the foreach hear the current
node in the node list (nodes[i]) is cast to an XmlElement interface so that the GetAttributes()
method can be used to retrieve the value of the current element’s “units” attribute.
If you rebuild and run the example after completing this code there shouldn’t be any change in
the output. However, if you run it through the debugger you can use a watch to verify that the
_parameters and _parameterUnits lists are populated in Main() after the call to
GetParameters().
Retrieve and display all the food names:
This is step 4 outlined in the Background and Overview section. Since we’ll be using an
XPathNavigator object to perform the XPath query, we’ll facilitate this by adding the following
using statement with the others at the top of the Program module:
using System.Xml.XPath;
This namespace isn’t required to run an XPath expression via the XmlDocument object.
Now complete the DisplayAll() helper method by adding the following code to the end of the
method body:
XPathNavigator nav = doc.CreateNavigator()!;
XPathNodeIterator nodeIt = nav.Select("//food/name");
while (nodeIt.MoveNext() && nodeIt.Current is not null)
Console.WriteLine($"\t{nodeIt.Current.Value}");
The XPathNavigator object must be created from the XmlDocument (DOM) object that contains
the data. This is done using the factory method CreateNavigator(). The navigator’s Select()
method will run an regular XPath expression that returns a node set. The node set is returned in
the form of an XPathNodeIterator. This is a “forward-only” iterator that won’t point to the first
item until MoveNext() is called on the iterator. MoveNext() will move to the next node in the
node set every time it’s called or will return false if there are no more nodes. The Current
property of the iterator returns another XPathNavigator object that references the current
node in the node set. This XPathNavigator object includes properties and methods for
accessing as well as for editing the current node. In the code above we’ve used the Value
property to access the text content of the current name element.
If you rebuild and run the program now, then select the option A from the main menu you
should get the following output:
All Foods (no predicate used)...
Avocado Dip
Bagels, New York Style
Beef Frankfurter, Quarter Pound
Chicken Pot Pie
Cole Slaw
Eggs
Hazelnut Spread
Potato Chips
Soy Patties, Grilled
Truffles, Dark Chocolate
This builds the XPath query, inserting the predicate based on user inputs, and assigns it to the
string variable query1.
Beneath this add the following code:
// Execute the main query and report the results
XPathNavigator nav = doc.CreateNavigator()!;
XPathNodeIterator nodeIt = nav.Select(query1);
if (nodeIt.Count > 0)
{
while (nodeIt.MoveNext() && nodeIt.Current is not null)
Console.WriteLine($"\t{nodeIt.Current.Value}");
}
else
Console.WriteLine("\tNo foods match that criterion.");
This code is similar to what we used in DisplayAll(). We added the if statement so that if the
query returns an empty node set then there will be special output to explain this to the user.
If you rebuild and run the program now, then select the option S from the main menu and
enter the inputs shown below you should get the same output:
1. total-fat
2. saturated-fat
3. cholesterol
4. sodium
5. carb
6. fiber
7. protein
Select one substance from the list above by entering its number: 4
1. exactly
2. less than
3. no more than
4. more than
5. no less than
Select one operator from the list above by entering its number: 2
Cole Slaw
Eggs
Hazelnut Spread
Truffles, Dark Chocolate
Retrieve the average amount of the nutritional substance in the selected foods:
This part of the example represents step 5c outlined in the Background and Overview section.
We’re including it to illustrate how XPath aggregate expressions may be executed using an
XPathNavigator object. In this case the aggregate expression will compute the average about of
the selected nutritional substance in the foods that were retrieved in the previous section.
In the DisplaySome() helper method, add the following code to the bottom of the if branch of
the if – else statement:
// Build aggregate XPath query to get the average amount of the substance
// in the foods
string query2 = "sum(//food[" + param + op + limit + "]/" + param + ")"
+ " div " + "count(//food[" + param + op + limit + "])";
Again, we had to build a suitable XPath expression from the param, op and limit variables.
XPath’s sum() function is used to add-up the amount of the nutritional substance in all the
selected foods that satisfy the predicate. XPath’s count function is used to determine how
many foods there are in this node set. XPath’s div operator divides the sum by the count to
return the average. Note that the XPathNavigator’s Evaluate() method is used here to execute
the XPath expression since it returns an aggregate value rather than a node set. This code was
added inside the if statement so that no average will be calculated if there are no foods to be
averaged which would cause a divide-by-zero error.
If you again rebuild and run the program, providing all the same user inputs you should have
following additional line of output at the bottom: