I have the following Xpath expression:
//*[not(input)][ends-with(@*, 'Copyright')]
I expect it to give me all elements - except input - with any attribute value which ends with "Copyright".
I execute it in the Selenium 2 Java API with webDriver.findElements(By.xpath(expression))
and get the following error:
The expression is not a legal
expression
But these expressions work without trouble:
//*[not(input)][starts-with(@*, 'Copyright')]
//*[ends-with(@*, 'Copyright')]
Any ideas?
I have the following Xpath expression:
//*[not(input)][ends-with(@*, 'Copyright')]
I expect it to give me all elements -
except input - with any attribute
value which ends with "Copyright".
There are a few issues here:
ends-with()
is a standard XPath 2.0 function only, so the chances are you are using an XPath 1.0 engine and it correctly raises an error because it doesn't know about a function called ends-with()
.
Even if you are working with an XPath 2.0 processor, the expression ends-with(@*, 'Copyright')
results in error in the general case, because the ends-with()
function is defined to accept atmost a single string (xs:string?
) as both of its operands -- however @*
produces a sequence of more than one string in the case when the element has more than one attribute.
//*[not(input)]
doesn't mean "select all elements that are not named input
. The real meaning is: "Select all elements that dont have a child element named "input".
Solution:
Use this XPath 2.0 expression: //*[not(self::input)][@*[ends-with(.,'Copyright')]]
In the case of XPath 1.0 use this expression:
....
//*[not(self::input)]
[@*[substring(., string-length() -8) = 'Copyright']]
Here is a short and complete verification of the last XPath expression, using XSLT:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:copy-of select=
"//*[not(self::input)]
[@*[substring(., string-length() -8)
= 'Copyright'
]
]"/>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<html>
<input/>
<a x="Copyright not"/>
<a y="This is a Copyright"/>
</html>
the wanted, correct result is produced:
<a y="This is a Copyright"/>
In the case of the XML document being in a default namespace:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="http://www.w3.org/1999/xhtml"
>
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/*">
<xsl:copy-of select=
"//*[not(self::x:input)]
[@*[substring(., string-length() -8)
= 'Copyright'
]
]"/>
</xsl:template>
</xsl:stylesheet>
when applied on this XML document:
<html xmlns="http://www.w3.org/1999/xhtml">
<input z="This is a Copyright"/>
<a x="Copyright not"/>
<a y="This is a Copyright"/>
</html>
the wanted, correct result is produced:
<a xmlns="http://www.w3.org/1999/xhtml" y="This is a Copyright"/>
I don't know Selenium but if //*[not(input)][starts-with(@*, 'Copyright')]
is parsed successfully and if additionally the XPath 2.0 function ends-with
is supported then I don't see any reason why //*[not(input)][ends-with(@*, 'Copyright')]
is not accepted as a legal expression. Your verbal description however sounds as if you want //*[not(self::input)][@*[ends-with(., 'Copyright')]]
.
//*[not(input)]
selects any elements not having any input child element while //*[not(self::input)]
selects any elements not being themselves input
elements. As for comparing [@*[ends-with(., 'Copyright')]]
with what you have, my suggestion is true as long as there is any attribute node which ends with 'Copyright' while your test would only work if there is a single attribute which ends with 'Copyright', as ends-with http://www.w3.org/TR/xquery-operators/#func-ends-with allow a sequence with a single item as its first argument or an empty sequence but not several items.
Most likely explanation is that you are using an XPath 1.0 processors. The ends-with() function requires XPath 2.0 support.
//*[not(self::input)][@*[substring(., string-length(.) -8) = 'Copyright']]
May be small correction with string-length(.)
Now, it may work.