views:

34

answers:

3

Let's say I have this document.

<a:Root>
    <a:A>
        <title><a:B/></title>
        <a:C>
            <item><a:D/></item>
        </a:C>
    </a:A>
</a:Root>

And I have an XmlNode set to the <a:A> element.

If I say

A.SelectNodes( "//a:*", namespaceManager )

I get B, C, and D. But I don't want D because it's nested in another "a:" element.

If I say

A.SelectNodes( "//a:*[not(ancestor::a:*)]", namespaceManager )

of course, I get nothing, since both A and its parent are in the "a" namespace.

How can I select just B and C, that is, the shallowest children matching the namespace?

Thanks.

Note, this is XPath 1.0 (.NET 2), so I can't use in-scope-prefixes (which it appears would help).

Also, this isn't really a question about namespaces. The quandary would be the same with other matching criteria.

+2  A: 

What about this:

<xsl:variable name="parents" select="ancestor-or-self::a:*" />
<xsl:value-of select="//a:*[not(deep-equal(ancestor::a:*, $parent))]" />

In XSLT this seems simple to do (store a node set as a variable), but I don't exactly know how to implement this in C#.

Edit: Working further on the idea of using count, this can probably work:

int nrParents = A.SelectNodes("ancestor-or-self::a:*", namespaceManager).Count(); // Or was it Size?
A.SelectNodes("//a:*[count(ancestor::a:*)!=" + nrParents + "]", namespaceManager)
Marc
Thanks for the reply. I'm not using XSLT here, or XPath 2.0. Indeed, .NET still does not support XPath 2.0.
harpo
`deep-equal` is XPath/XSLT 2.0
Pavel Minaev
@Pavel, I beg to differ. But in any case, I tried using deep-equal and .NET does not recognize it.
harpo
@harpo: it was a comment to Marc's reply, not to your comment (I didn't see it when I posted). It actually says the same thing that yours does :)
Pavel Minaev
I've added a new .NET option that can probably work :)
Marc
@Pavel, I see, I misunderstood.
harpo
+1 for the help. Something very close to this worked.
harpo
This is not an XSLT question.
Dimitre Novatchev
A: 

This isn't doable with a single XPath 1.0 expression. Extending on Marc's answer which uses XSLT, you can do this for 1.0:

<xsl:variable name="n" select="count(ancestor-or-self::a:*)" />
<xsl:variable name="result" select=".//a:*[count(ancestor::a:*) = $n]" />

or the equivalent sequence of C# calls for Select....

Pavel Minaev
Thanks, I phrased it slightly differently (and of course in C#), but the technique of comparing the counts was clever.
harpo
This is *not* an XSLT question.
Dimitre Novatchev
@Dimitre: that much is clear, but the corresponding C# code is trivial to write.
Pavel Minaev
+1  A: 

This is not an XSLT question, so here is a single XPath expression that selects the two nodes wanted:

/*/*/descendant::a:*[not(count(ancestor::a:*) > 2)]
Dimitre Novatchev
Thanks for the input, I went with something very much like this. But because the depth of the starting node was unknown, I did have to calculate it in a separate step.
harpo
@harpo: In your case, was the starting node known, eg. did you have an XPath expression selecting the starting node?
Dimitre Novatchev
No, I just had an XmlNode reference. Actually, I ended up building path expressions for the selected nodes using a routine adapted from Jon Skeet's answer to another question, http://stackoverflow.com/questions/241238/how-to-get-xpath-from-an-xmlnode-instance-c/241291#241291
harpo