【问题标题】:XPath returning string or boolean in VBA?XPath 在 VBA 中返回字符串或布尔值?
【发布时间】:2011-05-06 04:12:57
【问题描述】:

Word 宏执行 XPath 表达式的最简单方法是:

"string(/alpha/beta)" 

"not(string(/alpha/beta)='true')" 

应该分别返回字符串和布尔值? (相对于 xml 节点或节点列表)

我想避免运行 Office 2007 或 2010 的计算机上不存在的 DLL。

函数 selectSingleNode(queryString As String) 返回一个 IXMLDOMNode,所以不会这样做。

换句话说,类似于 .NET 的 xpathnavigator.evaluate [1],它是做什么的?

[1]http://msdn.microsoft.com/en-us/library/2c16b7x8.aspx

【问题讨论】:

    标签: vba vb6 ms-word xpath


    【解决方案1】:

    您可以使用 XSL 转换来评估 XPath 表达式,特别是 xsl:value-of

    我写了一个Evaluate 函数,就是按照这个原理工作的。它在内存中创建一个 XSL 样式表,其中包含一个 XSL 模板,该模板将采用 XPath 表达式,对其进行评估,并返回一个新的 XML 文档,该文档包含<result> 节点中的结果。它检查以确保 value-of 返回了某些内容(如果没有返回则抛出错误),如果是,则将 XPath 表达式的结果转换为以下数据类型之一:LongDouble、@987654331 @,或String

    以下是我用来练习代码的一些测试。我使用了您链接到的 MSDN 页面中的 books.xml 文件(如果要运行这些测试,您必须将路径更改为 books.xml)。

    Public Sub Test_Evaluate()
    
        Dim doc As New DOMDocument
        Dim value As Variant
    
        doc.async = False
        doc.Load "C:\Development\StackOverflow\XPath Evaluation\books.xml"
    
        Debug.Assert (doc.parseError.errorCode = 0)
    
        ' Sum of book prices should be a Double and equal to 30.97
        '
        value = Evaluate(doc, "sum(descendant::price)")
        Debug.Assert TypeName(value) = "Double"
        Debug.Assert value = 30.97
    
        ' Title of second book using text() selector should be "The Confidence Man"
        '
        value = Evaluate(doc, "descendant::book[2]/title/text()")
        Debug.Assert TypeName(value) = "String"
        Debug.Assert value = "The Confidence Man"
    
        ' Title of second book using string() function should be "The Confidence Man"
        '
        value = Evaluate(doc, "string(/bookstore/book[2]/title)")
        Debug.Assert TypeName(value) = "String"
        Debug.Assert value = "The Confidence Man"
    
        ' Total number of books should be 3
        '
        value = Evaluate(doc, "count(descendant::book)")
        Debug.Assert TypeName(value) = "Long"
        Debug.Assert value = 3
    
        ' Title of first book should not be "The Great Gatsby"
        '
        value = Evaluate(doc, "not(string(/bookstore/book[1]/title))='The Great Gatsby'")
        Debug.Assert TypeName(value) = "Boolean"
        Debug.Assert value = False
    
        ' Genre of second book should be "novel"
        '
        value = Evaluate(doc, "string(/bookstore/book[2]/attribute::genre)='novel'")
        Debug.Assert TypeName(value) = "Boolean"
        Debug.Assert value = True
    
        ' Selecting a non-existent node should generate an error
        '
        On Error Resume Next
    
        value = Evaluate(doc, "string(/bookstore/paperback[1])")
        Debug.Assert Err.Number = vbObjectError
    
        On Error GoTo 0
    
    End Sub
    

    这里是Evaluate 函数的代码(IsLong 函数是一个帮助函数,使数据类型转换代码更具可读性):


    注意: 正如 barrowc 在 cmets 中提到的那样,您可以通过将 DOMDocument 替换为特定于版本的类名来明确说明要使用的 MSXML 版本,例如DOMDocument30 (MSXML3) 或DOMDocument60 (MSXML6)。编写的代码将默认使用 MSXML3,它目前部署更广泛,但 MSXML6 具有更好的性能,并且是最新版本,是微软目前推荐的版本。

    有关不同版本的 MSXML 的更多信息,请参阅问题 Which version of MSXML should I use?


    Public Function Evaluate(ByVal doc As DOMDocument, ByVal xpath As String) As Variant
    
        Static styleDoc As DOMDocument
        Dim valueOf As IXMLDOMElement
        Dim resultDoc As DOMDocument
        Dim result As Variant
    
        If styleDoc Is Nothing Then
    
            Set styleDoc = New DOMDocument
    
            styleDoc.loadXML _
                "<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>" & _
                    "<xsl:template match='/'>" & _
                        "<result>" & _
                            "<xsl:value-of />" & _
                        "</result>" & _
                    "</xsl:template>" & _
                "</xsl:stylesheet>"
    
        End If
    
        Set valueOf = styleDoc.selectSingleNode("//xsl:value-of")
        valueOf.setAttribute "select", xpath
    
        Set resultDoc = New DOMDocument
        doc.transformNodeToObject styleDoc, resultDoc
    
        If resultDoc.documentElement.childNodes.length = 0 Then
            Err.Raise vbObjectError, , "Expression '" & xpath & "' returned no results."
        End If
    
        result = resultDoc.documentElement.Text
    
        If IsLong(result) Then
            result = CLng(result)
        ElseIf IsNumeric(result) Then
            result = CDbl(result)
        ElseIf result = "true" Or result = "false" Then
            result = CBool(result)
        End If
    
        Evaluate = result
    
    End Function
    
    Private Function IsLong(ByVal value As Variant) As Boolean
    
        Dim temp As Long
    
        If Not IsNumeric(value) Then
            Exit Function
        End If
    
        On Error Resume Next
    
        temp = CLng(value)
    
        If Not Err.Number Then
            IsLong = (temp = CDbl(value))
        End If
    
    End Function
    

    【讨论】:

    • 这正是我认为我可能必须这样做的方式。很高兴如此迅速地得到确认,并且非常惊喜地将实施交付在一个盘子上:-)
    • @plutext:没问题。这是一个有趣的问题,我可能会在即将到来的项目中使用它,所以我想我会写一些代码来看看它是否可行。 :-)
    • +1,但在使用 DOMDocument 而不是 DOMDocument60 时要小心谨慎。在这种情况下,DOMDocument 几乎肯定等于 DOMDocument30 - 参见 msdn.microsoft.com/en-us/library/ms757837%28v=VS.85%29.aspx - 也参见 stackoverflow.com/questions/951804/…
    • @barrowc:我认为使用 MSXML6 处理大型 XML 文档会有更好的性能,但 MSXML3 不是更广泛地部署吗?
    • @Mike Spross:MSXML3 确实比 MSXML6 更广泛地部署(从 Win2K SP4 开始作为标准)(从 Vista 开始作为标准;可以为 Win2K 和更高版本下载)。如果您想使用 MSXML3,您可以显式指定 DOMDocument30。我认为使用DOMDocument 可能会导致混淆,如果人们没有意识到这意味着DOMDocument30 并不意味着DOMDocumentxx 其中xx 是最新安装的MSXML 版本
    猜你喜欢
    • 2023-03-20
    • 2021-10-22
    • 2018-05-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-16
    • 2016-02-27
    • 2013-08-17
    相关资源
    最近更新 更多