DataStream
LANGUAGES: XML |XPath
ASP.NET VERSIONS: 1.x | 2.0
XPathBasics
IdentifyingNodes and Selections in XML Data
By BrianNoyes
XML datais becoming more prevalent as more businesses and systems become integratedover the Web. Happily, XML itself becomes easier to work with in every releaseof the .NET Framework and Microsoft SQL Server. The upcoming release of .NET2.0 and SQL Server 2005 (code-named Yukon) next year is no exception.
To workeffectively with XML, however, you need to get comfortable with a number ofrelated XML technologies, including XML Schema, XPath, XSLT, and - eventually -XQuery. This article focuses on the basic syntax of XPath. XPath isparticularly important because many of the other XML technologies (includingXSLT, XPointer, and XQuery) use XPath as the means to identify nodes andselections within XML data.
Location,Location, Location
XPath isall about specifying a location or set of locations (in the form of nodes)within a set of XML data or an XML document. Keep in mind that data can bepresented as XML even though it may have no relation to a physical file ordocument; the term XML document is still used to describe that set ofdata. XPath treats XML data as a hierarchical set of nodes through which youcan navigate or identify nodes based on their names and relative location (orpath) to one another.
Theeasiest analogy for understanding the syntax of basic XPath is to think aboutyour file system and the paths that you use to specify the location of files orfolders within the file system. In that case, the files and folders are thenode types, and folder nodes can contain other folder or file nodes, forming ahierarchical system of nodes that you can navigate. You specify the path to afile or folder either by fully qualifying it from its root (e.g. the C:\drive), or by using a relative path from a current location (e.g....\include\myfile.xml).
Youseparate each step in the hierarchical navigation scheme by separating it withbackslashes. These same concepts, and even a similar syntax, apply tospecifying the location of nodes within an XML document using XPath.
XPathgoes beyond specifying locations of nodes, however. XPath expressions can becomposed of complex conditions, or predicates, that are evaluated against anode or set of nodes to determine the result of the XPath expression. The XPathlanguage also includes a number of built-in functions that allow you to modifythe result of the expression in many ways.
XPathexpressions can evaluate to a node set, a number, a string, or a Boolean. Theyevaluate to a node set when the expression represents one or more nodes withinthe XML that match the criteria specified by the expression. They can evaluateto a number, string, or Boolean when they are used to extract the value of anode, or the result of a built-in function.
Location steps. The trick to understanding orconstructing an XPath expression is to break it down into its constituentparts. An XPath expression is composed of one to many location steps. Eachlocation step evaluates to either a node set, or a value. If the location stepevaluates to a node set, it can be followed by subsequent location steps thatare evaluated relative to the previous location step. In this way, you canthink of the node set results of a location step as a cursor or set of cursorsinto the XML document. These cursors set the current context for the evaluationof subsequent location steps.
Eachlocation step within an XPath expression is separated by a forward-slash. You canview everything between the forward slashes as individual location steps:
location-step-1/location-step-2/location-step-3...
In thisexample, location-step-2 would be evaluated relative to the nodes that were theresult of location-step-1, and location-step-3 would be evaluated relative tothe resulting nodes from location-step-2.
Eachlocation step can itself be composed of three parts: an axis, a node test, anda predicate. The axis specifies a search direction and depth from the currentnode. The node test is the heart of the location step, and is the part thatwill be evaluated against nodes along that axis to determine whether they matchthe criteria. The predicate is optional, and allows you to specify assertionsthat will be evaluated against any nodes matching the node test to furtherfilter the results.
Axes. The axes include child, parent,descendant, ancestor, and a number of others. Child means to only look at nodesthat are directly underneath the current node, and parent means to only look atthe node directly above the current node. Descendant means to look at all nodesat any depth beneath the current node, and ancestor means to look at all nodesthat are any depth above the current node - as long as there is a direct pathfrom that node to the current node. Axes are specified with their name,followed by the :: operator. For example:
parent::Element1
Thelocation step above will return a single node reference if the current node hasa parent element named Element1, and will return no node reference otherwise.The node reference returned will be to the Element1 element itself. TheElement1 part of the location step above is the node test. It tests the nodefound at the specified location for a match against the specified name. Elementsare specified by name; attribute names are prefaced by the @ symbol. Wildcardsare also allowed, using the * symbol. The child axis is implied if no axis isspecified in a location step. For example:
*/Album/@artistname
Theexpression above first matches all nodes that are children of the current node(the * wildcard), then matches all Album elements that are children of thosechild nodes, and finally matches the artistname attribute of the Album elementsfrom the previous two location steps, if such an attribute exists on the Albumelement. The resulting node set would contain references to the artistnameattribute nodes that satisfied the entire expression.
So ifyou start at the beginning of an XPath expression and take each location stepin turn, you can visualize the result of each location step as a set of zero tomany pointers to nodes within the document. Subsequent location steps areevaluated relative to those nodes, resulting in a new set of pointers, untilall the location steps have been evaluated. The final set of pointers to nodesis the resulting node set of the XPath expression.
Nodetests can also extract and return the text contents of a node, such as thevalue of an attribute, or the text contents of an element. You do this byspecifying text for the node test. For example:
Track/text()
Theexpression above would return the text contained inside the Track element. Youcan also return Boolean or numeric values from node tests using some of thebuilt-in functions of the XPath language, such as the contains method orthe count method, respectively.
Predicates. Appending a predicate to alocation step adds a significant amount of expressiveness to the XPathlanguage. Once the axis, node test, and previous location steps have set thecontext of a particular expression, a predicate can test assertions about theresulting nodes to further decide whether to declare a match or not. Predicatesare specified between square brackets immediately following the node test. Theydo not change the current context of the expression, but they are evaluatedrelative to the context set by the location step. For example:
Track[@number='1']
Theexpression above matches any Track elements that are children of the currentnode (again, the child:: axis is implied if none is specified) who have anattribute, named number, whose value is 1. The returned nodes are Trackelements, not number attributes. The predicate is used to further restrict theresults of the expression, but does not change the context or node type thatthe expression will return.
Multiplepredicates can be strung together and represent an implicit AND Booleanoperation. For example:
Track[@number='1'][text()='Forever']
Theexpression above would match all child Track elements whose number attributewas equal to 1, and whose text content was equal to Forever. You can also combine Boolean operators within a predicate toconstruct arbitrarily complex assertions about the nodes that an XPathexpression will match.
The effect of namespaces. The last thing to understand aboutXPath expressions is the effect that namespaces have on the specification ofnodes within an expression. If elements or attributes within an XML documentare scoped to a particular namespace, that namespace becomes part of the fullyqualified name of that node. To specify that node as part of an XPathexpression, you therefore need to include that namespace information as part ofthe node name.
The waythis is handled is going to depend on what processing engine you're using toconsume the XPath expressions. Typically, the way it works is that you'll needto associate a prefix with each namespace in the document from which you needto specify nodes. You then use that prefix as part of the node name in theXPath expression. For example:
descendant::musicns::Track
Theexpression above would match any Track elements underneath the current node atany level, where the Track element is declared within the namespace identifiedwith the musicns prefix.
Theassociation between a prefix and the actual namespace URI must be done throughsome means specific to the processing context. In the case of querying XMLusing XPath expressions in .NET, you do this by adding prefix and namespace URIpairs to an XmlNamespaceManager instance, then associating thatnamespace manager with the expression you are evaluating.
Step by step. Again, the key to understandingany XPath expression is to first take it one location step at a time, and thento break each location step into its axis, its node test, and its predicate(s).Starting with the first location step, determine what the resulting set ofnodes is. Evaluate the successive location steps relative to the results of thepreceding ones, until the entire XPath expression has been evaluated.
The downloadcode for this article includes a simple .NET WinForms XPath Query Analyzerapplication that will allow you to play around with XPath expressions. You canload an XML document into the form, then enter XPath expressions to query thedocument (see Figure 1). The application will highlight the matching nodes bychanging the font size and color. The application is not very efficient withthe rendering of the document, so I don't recommend loading large documentsinto it. But it's a very handy little tool for trying out XPath expressionsagainst a document to visualize what the results will be.
Figure 1: The XPath Query Analyzerapplication.
The bestplace to look for more information on XPath, including lots of examples, is inthe MSXML 4.0 SDK documentation in MSDN. Under the XPath sections, there iscomprehensive coverage of all the various combinations of axes, node tests,predicates, and built-in functions.
If youaren't already comfortable with XPath expressions, now is a great time to startgetting used to them. They are vital to querying XML documents in .NET today,and are used by a number of related XML technologies that you may need now andin the future. In the next issue I'll cover how to execute XPath expressions in.NET to perform queries against XML data in memory.
Thefiles referenced in this article are available for download.
BrianNoyes is asoftware architect with IDesign, Inc. (http://www.idesign.net),a .NET-focused architecture and design consulting firm. Brian is a MicrosoftMVP in ASP.NET who specializes in designing and building data-drivendistributed Windows and Web applications. Brian writes for a variety ofpublications and is working on a book for Addison-Wesley on building Windows FormsData Applications with .NET 2.0. Contact him at mailto:brian.noyes@idesign.net.