XmlNode
defines
two methods, with two overloads each, to allow navigation via XPath.
SelectSingleNode( )
returns a single
XmlNode
that matches the given XPath, and
SelectNodes( )
returns an
XmlNodeList
.
SelectSingleNode
( ) returns a single
XmlNode
that matches the given XPath expression.
If more than one node matches the expression, the first one is
returned; the definition of “first”
depends on the order of the axis used. The context node of the XPath
query is set to the XmlNode
instance on which the
method is invoked.
One overload of
SelectSingleNode( )
takes just the XPath
expression. The other one takes the XPath expression and an
XmlNamespaceManager
. The
XmlNamespaceManager
is used to resolve any
prefixes in the XPath expression.
Example 6-2 shows a simple program that selects a single node from an XML document and writes it to the console, with human-readable formatting.
using System; using System.Xml; using System.Xml.XPath; public class XPathQuery { public static void Main(string [ ] args) { string filename = args[0]; string xpathExpression = args[1]; XmlDocument document = new XmlDocument( ); document.Load(filename); XmlTextWriter writer = new XmlTextWriter(Console.Out); writer.Formatting = Formatting.Indented; XmlNode node = document.SelectSingleNode(xpathExpression); node.WriteTo(writer); writer.Close( ); } }
Because SelectSingleNode( )
is called on the
XmlNode
instance that represents the entire
document, the context node in this case is the
document, and the XPath query will be executed relative to the entire
document. However, the context node could be any other
XmlNode
in the document, depending on which
XmlNode
instance’s
SelectSingleNode( )
method is invoked.
If you need to use a specific XmlNode
subclass’s methods or properties on the resulting
XmlNode
instance, it is up to the calling code to
cast the XmlNode
instance to the appropriate type.
For example, if the XPath expression were to return an
XmlElement
instance, the code would explicitly
have to cast the XmlNode
to
XmlElement
in order to call, for example,
GetAttribute( )
on it. But then you could also
construct your XPath expression to just go ahead and select the
attribute in question
directly.
Casting an XmlNode
to the incorrect type will
cause an InvalidCastException
to be thrown. You
should be sure you know what type of XmlNode is returned by
SelectSingleNode( )
before casting. Two ways to determine
an object instance’s actual type are the
typeof
operator and the GetType(
)
method. You can also use the as
operator to perform a typesafe cast. For more information on
determining an instance’s type at runtime, see
C# Essentials, 2nd Edition
(O’Reilly).
SelectNodes( )
is
similar to SelectSingleNode( )
, except that it
returns an XmlNodeList
rather than an
XmlNode
. XmlNodeList
implements
IEnumerable
, so you can use any of the techniques
commonly used to manage a collection. For example, you can
interrogate the XmlNodeList
’s
Count
property to discover how many elements it
contains, access each element with its array indexer, or use a
foreach
statement to iterate through the elements
in order.
I’ve modified the SelectSingleNode(
)
example to select a list of nodes. The changed lines are
highlighted:
XmlDocument document = new XmlDocument( ); Document.Load(filename); XmlTextWriter writer = new XmlTextWriter(Console.Out); writer.Formatting = Formatting.Indented; XmlNodeList nodeList = document.SelectNodes(xpathExpression); Console.WriteLine("{0} nodes matched.", nodeList.Count); foreach (XmlNode node in nodeList) { node.WriteTo(writer); } writer.Close( );
As with
SelectSingleNode( )
, it is up to the calling code
to ensure that any casts are correct; this includes the implicit cast
in the foreach
statement. In this example,
however, the foreach
will always succeed, because
each element of an XmlNodeList
is, by definition,
an instance of XmlNode
.
Also like
SelectSingleNode( )
, one overload of
SelectNodes( )
takes an
XmlNamespaceManager
parameter.
In
addition to the SelectSingleNode( )
and
SelectNodes( )
methods, XmlNode
implements the IXPathNavigable
interface. The only
method that IXPathNavigable
requires is
CreateNavigator( )
. CreateNavigator(
)
returns an XPathNavigator
, which
provides an efficient, read-only, random-access model of the
XmlNode
. Once you have an
XPathNavigator
, you can call its Select(
)
method to navigate to a node or set of nodes using an
XPath expression.
The following code produces the same output as the
SelectNodes( )
example above:
XmlDocument document = new XmlDocument( ); document.Load(filename); XmlTextWriter writer = new XmlTextWriter(Console.Out); writer.Formatting = Formatting.Indented; XPathNavigator navigator = document.CreateNavigator( ); XPathNodeIterator iterator = navigator.Select(xpathExpression); Console.WriteLine("{0} nodes matched.", iterator.Count); while (iterator.MoveNext( )) { XmlNode node = ((IHasXmlNode)iterator.Current).GetNode( ); node.WriteTo(writer); } writer.Close( );
A couple of lines in this example bear closer investigation:
XPathNodeIterator iterator = navigator.Select(xpathExpression);
XPathNavigator
has a Select(
)
method that returns an
XPathNodeIterator
. Select( )
itself has two overloads, one that takes a string
XPath expression, and one that takes a precompiled
XPathExpression
. A compiled
XPathExpression
can be obtained by passing a
textual XPath expression to the XPathNavigator.Compile(
)
method.
If you were going to select the same XPath expression multiple times
or from different context nodes, it would be more efficient to use a
compiled XPathExpression
. In the previous example,
I used the version of Select( )
that takes a
string
parameter.In the following example,
I’m using a compiled
XPathExpression
:
XPathExpression expression = navigator.Compile(xpathExpression); XPathNodeIterator iterator = navigator.Select(expression);
Other useful methods
that XPathExpression
provides include
AddSort( )
, whose two overloads allow you to sort
the set of nodes resulting from the expression, and
SetContext( )
, which allows you to set the
XmlNamespaceManager
used to look up namespace
prefixes.
In addition to the two
Select( )
overloads,
XPathNavigator
provides a set of selector methods,
including SelectAncestors( )
,
SelectChildren( )
, and SelectDescendants(
)
, each of which returns an
XPathNodeIterator
ready for use in navigating the
set of results.
SelectChildren( )
selects only from among the
direct child nodes of the context node, while
SelectDescendants( )
selects from among the
context node’s direct children, plus their children,
and so on.
Each of
these methods has two overloads; one of which takes an
XPathNodeType
, while the other takes a
string
local name and namespace URI. In all these
methods, the parameters determine which nodes will be selected,
relative to the XPathNavigator
’s
context node:
while (iterator.MoveNext( )) {
XPathNodeIterator
represents a set of nodes returned
from an XPath expression. Its interesting methods include
Clone( )
and MoveNext( )
.
Clone( )
, which implements the .NET
Framework’s ICloneable
interface,
returns a new XPathNodeIterator
whose state is the
same as the original one, but further changes to either the original
or the clone will not affect the other. Thus, cloning an
XPathNodeIterator
allows you to work with XPath
query results by allowing you to navigate through different branches
of the XML tree. MoveNext( )
moves the
XPathNodeIterator
’s position to
the next node.
XPathNodeIterator
’s
interesting properties include Count
,
Current
, and CurrentPosition
.
Current
returns a new
XPathNavigator
whose context node is the
XPathNodeIterator
’s current node.
Like cloning an XPathNodeIterator
, this allows you
to navigate through XML branches. Count
and
CurrentPosition
return the number of nodes
selected and the
XPathNodeIterator
’s current
position, respectively:
XmlNode node = ((IHasXmlNode)iterator.Current).GetNode( );
Since the
XPathNavigator
in this example was obtained by
calling XmlNode.GetNavigator( )
, it implements the
IHasXmlNode
interface.
IHasXmlNode
’s sole method,
GetNode( )
, returns the
XPathNodeIterator
’s context node.
So this line of code gets an XPathNavigator
whose
context node is the same as the XPathIterator
instance’s current node and casts it to an
IHasXmlNode
in order to get its current
XmlNode
.
It’s
important to remember the distinction between the
XPathNodeIterator.Current
property, which returns
a new XPathNavigator
positioned at the context
node, and the IHasXmlNode.GetNode( )
method, which
returns the context node as an XmlNode
. By casting
an XPathNavigator
to an
IHasXmlNode
, you can get access to the current
XmlNode
itself.
18.118.1.232