3
votes

I'm using VBA in Excel 2003 to isolate some xml nodes. Everything is peachy until the 'contains' method is called in an xpath wildcard search where the 'unknown method' error is returned.

The code to load the xml file is:

Public Function LoadXml()

    strPath = ThisWorkbook.Path & "\V1.xdp"
    Set docXml = New MSXML2.DOMDocument
    docXml.Load (strPath)
    Set LoadXml = docXml

End Function

The code to isolate the node is:

Public Sub TestXpath()
    Dim docXml              As MSXML2.DOMDocument
    Dim NodeList            As IXMLDOMSelection
    Dim CurrNode            As IXMLDOMNode
    Dim n                   As Long

    'Identify xpath
    Dim strXPath As String
    strXPath = "//event/script[contains (text(),'validationScript.errorCount')]"

    'Loop through nodes and inspect attributes to ensure we've specified the correct XPath
    Set docXml = LoadXml
    Set NodeList = docXml.SelectNodes(strXPath)

    For n = 0 To (NodeList.Length - 1)
        Set CurrNode = NodeList.Item(n)
        InspectNode CurrNode
    Next

ExitSub:

    Set docXml = Nothing
    Exit Sub

End Sub

I've got a reference set to MSXML v6. Any idea why I'm getting the 'unknown method' error?

Thanks

Jon

1
Thanks for the edit paul t - John ONeil

1 Answers

5
votes

Using the XPath higher functions

Microsoft developed their XML parser before W3C finalised XPath standards, so it was based on their own system called XSL Pattern. (Microsoft was involved in defining the XPath standards so XSL Pattern is similar and much of it, though not all, became part of XPath.) For backwards compatibility, Microsoft’s XML parser (even the latest one) uses XSL Pattern by default. XSL Pattern doesn’t include the functions such as ‘contains’, which is why the ‘unknown method’ error occurs. To fix this, we need to tell the DOMDocument object (docXml) to use XPath instead: docXml.setProperty "SelectionLanguage", "XPath"

Namespaces

However, this creates another problem, because XPath is more sensitive to namespaces. The path //event, for example, finds all descendents of the root node which are called ‘event’ and have no namespace. Therefore the statement docXml.selectNodes(“//event”) returns no results, because the form’s XML has a default namespace: . To deal with this, we need to tell the DOMDocument object about the namespaces in use and map them to prefixes. After a lot of web searching, it seems that the way to do this is as follows:

Const strNamespaces As String = "xmlns:xfa='http://www.xfa.org/schema/xfa-template/2.8/' xmlns:xdp='http://ns.adobe.com/xdp/'"

docXml.setProperty "SelectionNamespaces", strNamespaces

Note that the prefixes defined here don’t have to match those used in the XML, which is just as well because the default namespace has no prefix. We can then use these prefixes in our XPath statements. For example: docXml.selectNodes(“//xfa:event/xfa:script[contains (text(),'validationScript.errorCount')]”) docXml.selectNodes(“//xfa:event[@activity = 'initialize']/xfa:script”)

These now work. Note that attributes don’t inherit the default namespace and don’t therefore have the ‘xfa’ suffix.