James Dunn
James Dunn

Reputation: 8284

XPath select child if child exists, else select parent

I am dealing with elements that sometimes contain their own text, and sometimes contain a span with the text. I'm generating an xpath in my program to get the elements I need based on the title attribute, so I'm using a template like this:

//div[@class='grid-canvas']//*[@title='Row %d column %s']

This works perfectly for elements that have their own text. But if they keep their text inside a child span, then I have to change my xpath template:

//div[@class='grid-canvas']//*[@title='Row %d column %s']/span

I really just want to use one template for my xpath. Since I either want the parent if it has no children, or the child element (which is a span) if it exists, I figured something like .[not(node())] | span should work, so I tried this:

//div[@class='grid-canvas']//*[@title='Row %d column %s']/.[not(node())] | span

But it didn't work. I can't figure out what I'm missing. What am I doing wrong?

Upvotes: 1

Views: 2088

Answers (1)

Jens Erat
Jens Erat

Reputation: 38732

Your third query start searching at the current context again – if you didn't set that somewhere else, it will be scan for a <span/> root element.

XPath 1.0 does not allow alternations "in the middle" or at the end of a path, only as prefixes, so you will have to repeat the whole path:

//div[@class='grid-canvas']//*[@title='Row %d column %s']/span |
//div[@class='grid-canvas']//*[@title='Row %d column %s'][not(span)]

This will select all "sub"spans and those parent elements not containing a span element.

In XPath 2.0, you'd be able to do this if only one (or none) span elements exist:

//div[@class='grid-canvas']//*[@title='Row %d column %s']/(span, .)[1]

or for arbitrary numbers:

//div[@class='grid-canvas']//*[@title='Row %d column %s']/(span, .[not(span)])

Upvotes: 1

Related Questions