2010-11-07 2 views
2

Что это самый простой способ для макроса Word, чтобы выполнить XPath выражения, такие как:XPath возвращает строку или boolean в VBA?

"string(/alpha/beta)" 

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

, которая должна возвращать строку и логическое соответственно? (В отличие от узла XML или список узлов)

Я хочу, чтобы избежать библиотеки DLL, которые не будут уже присутствовать на компьютере под управлением Office 2007 или 2010.

Функция SelectSingleNode (QueryString As String) возвращает IXMLDOMNode , так что этого не произойдет.

Другими словами, что-то похожее на xpathnavigator.evaluate .NET [1], что делает это?

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

ответ

3

Вы можете использовать XSL преобразования для оценки XPath выражений, в частности xsl:value-of.

Я написал функцию Evaluate, которая работает по этому принципу. Он создает таблицу стилей XSL в памяти, которая содержит шаблон XSL, который будет принимать выражение XPath, оценивать его и возвращать новый XML-документ, содержащий результат в узле <result>. Он проверяет, чтобы value-of что-то возвращал (и выдает ошибку, если нет), и если да, то он преобразует результат выражения XPath в один из следующих типов данных: Long, Double, Boolean, или String.

Вот несколько тестов, которые я использовал для осуществления кода. Я использовал файл books.xml на странице MSDN, с которой вы связались (вам нужно будет изменить путь к 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 упоминается в комментариях, вы можете указать, какую версию MSXML вы хотите использовать, заменив DOMDocument на имя класса для версии, например DOMDocument30 (MSXML3) или DOMDocument60 (MSXML6). В написанном коде по умолчанию будет использоваться MSXML3, который в настоящее время более широко развернут, но MSXML6 имеет лучшую производительность и, будучи последней версией, тот, который в настоящее время рекомендует Microsoft.

Для получения дополнительной информации о различных версиях 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 
+0

Это именно то, как я думал, что я мог бы сделать это. Приятно, чтобы это было подтверждено так быстро, и очень приятный сюрприз для реализации на блюде :-) – JasonPlutext

+0

@plutext: проблем нет. Это был интересный вопрос и что-то, что я мог бы использовать в предстоящем проекте, поэтому я подумал, что напишу какой-нибудь код, чтобы убедиться, что это выполнимо. :-) –

+0

+1 но с небольшой осторожностью при использовании 'DOMDocument', а не' DOMDocument60'. В этом контексте 'DOMDocument' почти наверняка равен' DOMDocument30' - см. Http://msdn.microsoft.com/en-us/library/ms757837% 28v = VS.85% 29.aspx - см. также http://stackoverflow.com/questions/951804/which-version-of-msxml-should-i-use – barrowc

Смежные вопросы