Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XPath expression for finding an element without a particular attribute

Tags:

c#

xpath

I have an xml file with the following format

<someXML>
    <a>
        <b></b>
        <b test='someValue'></b>
    </a>

    <c>
        <d test='someOtherValue'></d>
        <d></d>
    </c>
</someXML>

I'm trying to write C# code that will get me a node set containing only those nodes with the test attribute. I am trying to use an XPathExpression in this manner:

XDocument document = XDocument.Load("someXML");
XPathNavigator navigator = document.CreateNavigator();
XPathExpression expression = XPathExpression.Compile("//*[@test]");
object nodeSet = navigator.Evaluate(expression);

This doesn't seem to be working. When I debug, the nodeSet always contains the Root element. Is there something wrong with the expression or is this working correctly?

Thanks!

like image 225
Duke Cyrillus Avatar asked Feb 16 '26 13:02

Duke Cyrillus


2 Answers

Do you assume that the nodeSet contains the “Root” element because the debugger displays its string representation as Position=0, Current={Root}? That may be misleading; the reason that the “Root” element appears is because the XPathNodeIterator (which is the actual type returned by Evaluate in your case) has not yet been positioned on the first node resulting from the query. Per the MSDN documentation for XPathNodeIterator:

An XPathNodeIterator object returned by the XPathNavigator class is not positioned on the first node in a selected set of nodes. A call to the MoveNext method of the XPathNodeIterator class must be made to position the XPathNodeIterator object on the first node in the selected set of nodes.

You can retrieve your path items by iterating over your nodeSet:

XDocument document = XDocument.Load("someXML");
XPathNavigator navigator = document.CreateNavigator();
XPathExpression expression = XPathExpression.Compile("//*[@test]");
XPathNodeIterator nodeSet = (XPathNodeIterator)navigator.Evaluate(expression);
foreach (XPathItem item in nodeSet)
{
    // Process item here
}

Or, if you want them converted to an array:

XDocument document = XDocument.Load("someXML");
XPathNavigator navigator = document.CreateNavigator();
XPathExpression expression = XPathExpression.Compile("//*[@test]");
XPathNodeIterator nodeSet = (XPathNodeIterator)navigator.Evaluate(expression);
XPathItem[] items = nodeSet.Cast<XPathItem>().ToArray();
like image 96
Douglas Avatar answered Feb 18 '26 03:02

Douglas


Your XPath does work, it's just you need to retrieve nodes a bit differently:

XPathNavigator navigator = document.CreateNavigator();
XPathExpression expression = XPathExpression.Compile("//*[@test]");
var matchedNodes = navigator.Select(expression);
foreach (XPathNavigator node in matchedNodes)
{
    Console.WriteLine(node.UnderlyingObject);
}

Snippet above will print the following:

<b test='someValue'></b>
<d test='someOtherValue'></d>
like image 42
k.m Avatar answered Feb 18 '26 03:02

k.m