Reputation: 10985
I have the following XPath expression:
//a[@attribute='my-attribute']
When I have the following element in the HTML that XPath is searching, it matches as expected:
<a attribute="my-attribute">Some text</a>
But if there is an <svg>
tag under that element, XPath returns no match:
<a attribute="my-attribute">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"
viewBox="0 0 24 24" focusable="false"></svg>
</a>
Why doesn't XPath match in this case? Is there a way I can modify my expression to make it match?
EDIT:
Apparently it has to do with the namespace on the <svg>
element. Using the local-name()
function makes it match in the XPath tester I'm using:
//*[local-name()='a' and @attribute='my-attribute']
However, this still doesn't match when running through Selenium WebDriver. Any idea of how to get this working with Selenium?
Upvotes: 1
Views: 1125
Reputation: 41
Following is a possible solution in vb.net.
Public Class XmlNodeListWithNamespace
' see https://stackoverflow.com/questions/55385520/xpath-doesnt-match-when-desired-element-contains-child-elements
' @JaSON I would have thought the same thing,
' but removing the xmlns attribute from the svg tag
' causes the //a[@attribute='my-attribute'] expression to match.
' – Andrew Mairose
' Mar 28 at 13:07 "Asked 5 months ago Active 5 months ago" implies 2019-03-28 13:07.
' Therefore, I first considered deleting all occurrences of
' xmlns="" and xmlns="http://www.w3.org/1999/xhtml"
' I did this using the following Replacement.
' gstrHtml = Regex.Replace(
' input:=gstrHtml,
' pattern:=" *xmlns=""[^""]*""",
' replacement:="",
' options:=RegexOptions.IgnoreCase
' )
' However, the solution below retains the namespace, while avoiding unsightly xpath strings.
''' <summary>
''' For a given xpath, returns an XmlNodeList, taking account of the xmlns namespace.
''' </summary>
''' <param name="oXmlDocument">The current XML document.</param>
''' <param name="xpath">A normal xpath string, without any namespace qualifier.</param>
''' <returns>The XmlNodeList for the given xpath.</returns>
Public Shared Function NodeList(
oXmlDocument As XmlDocument,
xpath As String
) As XmlNodeList
Dim strXpath As String = xpath
' Insert Namespace Qualifier. For example,
' "//pre" becomes "//x:pre"
' "/html/body/form/div/pre" becomes "/x:html/x:body/x:form/x:div/x:pre"
' "//div[@id='nv_bot_contents']/pre" becomes "//x:div[@id='nv_bot_contents']/x:pre"
' "//div[@id='nv_bot_contents']/pre[@data-xxx='X2']" becomes "//x:div[@id='nv_bot_contents']/x:pre[@data-xxx='X2']"
' "//div[@id='nv_bot_contents']/pre[@data-xxx]" becomes "//x:div[@id='nv_bot_contents']/x:pre[@data-xxx]"
' "//pre[@data-xxx]" becomes "//x:pre[@data-xxx]"
strXpath = Regex.Replace(
input:=strXpath,
pattern:="(/)(\w+)",
replacement:="$1x:$2"
)
' See https://stackoverflow.com/questions/40796231/how-does-xpath-deal-with-xml-namespaces/40796315#40796315
Dim oXmlNamespaceManager As New XmlNamespaceManager(nameTable:=oXmlDocument.NameTable)
oXmlNamespaceManager.AddNamespace("x", "http://www.w3.org/1999/xhtml")
Dim oXmlNodeList As XmlNodeList = oXmlDocument.SelectNodes(
xpath:=strXpath,
nsmgr:=oXmlNamespaceManager
)
Return oXmlNodeList
End Function
End Class
Sample invocation:
Dim oXmlNodeList As XmlNodeList =
XmlNodeListWithNamespace.NodeList(
oXmlDocument:=oXmlDocument,
xpath:="//pre"
)
Upvotes: 0
Reputation: 111491
You may be confused by how the XPath hosting environment is presenting the selected a
elements.
Adding an svg
element to the a
element will not affect what's selected by
//a[@attribute='my-attribute']
In the case of
<a attribute="my-attribute">Some text</a>
the a
element has a string value consisting of more than just white space characters, but with
<a attribute="my-attribute">
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%"
viewBox="0 0 24 24" focusable="false"></svg>
</a>
the a
element has a string value that consists only of whites space, so for text results of the selection, you wouldn't see anything selected.
If you evaluate count(//a[@attribute='my-attribute'])
, you'll likely see the same results for both cases.
Upvotes: 3