使用Microsoft Xml Parser MSXML2如何使用XPath检索属性的值?

时间:2015-03-05 21:54:12

标签: xml vba xpath

这是另一个关于如何获取属性值的问题。我知道使用Microsoft的MSXML2 SelectNodes和SelectSingleNode方法时存在限制,因为它们必须返回一个节点集。

我曾经以为在表达式末尾使用text()并没有返回一个节点集,但后来我发现了IXMLDOMText类型,它可以看作是IXMLDOMAttribute类型的子节点,让我想知道是不是真的可以使用XPath来挖掘属性的值。

IXMLDOMText是一个节点,而不是文字,因此(理论上)它可以参与节点集,即节点集可以包含一个IXMLDOMText。

这是一些实验性代码。它在Excel VBA中运行,您需要设置工具 - >参考

你可以在底部看到我的尝试注释了我的尝试。任何聪明的人都知道答案吗?

我希望能够在节点上调用.xml方法并获取属性的值,目前这样做会给出键值和值对,而不仅仅是值。

对版主而言,请不要将其视为副本,因为我认为我已经发现了一个新的角度。

以下SO文章都建议将Xpath包装在string()中,但这对MSXML2.SelectNodes不起作用,因为它必须返回一个节点集
Getting attribute using XPath
Extract value of attribute node via XPath
How to get attribute value from node using xpath?

Option Explicit


Sub Test()
    '* Requires Tools->References-> Microsoft XML, v6.0


    Dim doc As MSXML2.DOMDocument60
    Set doc = New MSXML2.DOMDocument60

    '* THE OBJECTIVE IS TO INVENT AN XPATH THAT RETRIEVES THE VALUE OF AN ATTRIBUTE USING SOMETHING LIKE /a/c/@id/text()

    Dim s As String
    s = _
        "<a>" & _
            "<b>1stbText" & _
            "</b>" & _
            "<b>2ndbText" & _
            "</b>" & _
            "<c id='5'>cText" & _
            "</c>" & _
        "</a>"

    doc.LoadXML s
    Debug.Assert doc.parseError = 0

    TestB doc
    TestA doc
    TestC doc
    TestCID doc
    TestCIDText doc
End Sub

Sub TestB(ByVal doc As MSXML2.DOMDocument60)
    Dim xmlBTextNodes As MSXML2.IXMLDOMNodeList
    Set xmlBTextNodes = doc.SelectNodes("/a/b/text()")
    Debug.Assert Not xmlBTextNodes Is Nothing
    Debug.Assert xmlBTextNodes.Length = 2
    Debug.Assert xmlBTextNodes(0).Text = "1stbText"
    Debug.Assert xmlBTextNodes(1).Text = "2ndbText"

    Debug.Assert TypeName(xmlBTextNodes(0)) = "IXMLDOMText"

    Dim xmlCastToText As MSXML2.IXMLDOMText
    Set xmlCastToText = xmlBTextNodes(0)
    Debug.Assert xmlCastToText.xml = "1stbText"
    Debug.Assert xmlCastToText.Text = "1stbText"
End Sub

Sub TestA(ByVal doc As MSXML2.DOMDocument60)
    Dim xmlATextNodes As MSXML2.IXMLDOMNodeList
    Set xmlATextNodes = doc.SelectNodes("/a/text()")
    Debug.Assert Not xmlATextNodes Is Nothing
    Debug.Assert xmlATextNodes.Length = 0  '* interesting
End Sub

Sub TestC(ByVal doc As MSXML2.DOMDocument60)
    Dim xmlCTextNodes As MSXML2.IXMLDOMNodeList
    Set xmlCTextNodes = doc.SelectNodes("/a/c/text()")
    Debug.Assert Not xmlCTextNodes Is Nothing
    Debug.Assert xmlCTextNodes.Length = 1
    Debug.Assert xmlCTextNodes(0).xml = "cText"
    Debug.Assert xmlCTextNodes(0).Text = "cText"
    Dim xmlCastToText As MSXML2.IXMLDOMText
    Set xmlCastToText = xmlCTextNodes(0)
    Debug.Assert xmlCastToText.xml = "cText"
    Debug.Assert xmlCastToText.Text = "cText"
End Sub



Sub TestCID(ByVal doc As MSXML2.DOMDocument60)
    Dim xmlCIDNodes As MSXML2.IXMLDOMNodeList
    Set xmlCIDNodes = doc.SelectNodes("/a/c/@id")
    Debug.Assert Not xmlCIDNodes Is Nothing
    Debug.Assert xmlCIDNodes.Length = 1
    Debug.Assert xmlCIDNodes(0).xml = "id=""5"""
    Debug.Assert xmlCIDNodes(0).Text = "5"

    Debug.Assert TypeName(xmlCIDNodes(0)) = "IXMLDOMAttribute"


    Debug.Assert xmlCIDNodes(0).ChildNodes.Length = 1

    Dim xmlCastToText As MSXML2.IXMLDOMText
    Set xmlCastToText = xmlCIDNodes(0).ChildNodes(0)
    Debug.Assert xmlCastToText.xml = "5"
    Debug.Assert xmlCastToText.Text = "5"

    Debug.Assert xmlCastToText.ChildNodes.Length = 0

End Sub

Sub t2()
    '* a convenient entry so I can press F5 and run in the same vicinity as Sub TestCIDText()
    Test
End Sub

Sub TestCIDText(ByVal doc As MSXML2.DOMDocument60)
    Dim xmlCIDTextNodes As MSXML2.IXMLDOMNodeList

    '*** crucial XPath where I'd like to retrieve the text of an attribute
    'Set xmlCIDTextNodes = doc.SelectNodes("/a/c/@id/text")  '* doesn't work, returns 0 nodes
    'Set xmlCIDTextNodes = doc.SelectNodes("/a/c/@id/text()")  '* doesn't work, returns 0 nodes
    'Set xmlCIDTextNodes = doc.SelectNodes("string(/a/c/@id)")  '* doesn't work, throws "Expression must evaluate to a node-set. -->string(/a/c/@id)<--"


    Debug.Assert Not xmlCIDTextNodes Is Nothing
    Debug.Assert xmlCIDTextNodes.Length = 1  '<========stops here because of XPath not working

    Debug.Assert xmlCIDTextNodes(0).xml = "5"  '<== we can predict these results from the tail of Sub TestCID
    Debug.Assert xmlCIDTextNodes(0).Text = "5"

    Debug.Assert xmlCIDTextNodes(0).ChildNodes.Length = 0


End Sub

1 个答案:

答案 0 :(得分:0)

Sub Test()
    '* Requires Tools->References-> Microsoft XML, v6.0
    Dim doc As MSXML2.DOMDocument60
    Dim nl As MSXML2.IXMLDOMNodeList
    Dim s As String, n As MSXML2.IXMLDOMNode
    Dim v


    Set doc = New MSXML2.DOMDocument60

    s = "<a>" & _
            "<b>1stbText" & _
            "</b>" & _
            "<b>2ndbText" & _
            "</b>" & _
            "<c id='5'>cText" & _
            "</c>" & _
        "</a>"

    doc.LoadXML s

    'if possibly multiple matches...
    Set nl = doc.SelectNodes("/a/c/@id")
    For Each n In nl
        Debug.Print n.NodeValue ' >> "5" 
    Next n

    'if expecting just one match...
    Set n = doc.SelectSingleNode("/a/c/@id")
    If Not n Is Nothing Then Debug.Print n.NodeValue '>> "5" 

    'or?
    Debug.Print doc.SelectSingleNode("/a/c/@id").Text

End Sub