【问题标题】:With Microsoft Xml Parser MSXML2 how to retrieve value of an attribute using XPath?使用 Microsoft Xml Parser MSXML2 如何使用 XPath 检索属性的值?
【发布时间】:2015-03-05 21:54:12
【问题描述】:

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

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

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

这是一些实验代码。它在 Excel VBA 中运行,您需要设置一个 Tools->Reference

您可以在底部看到我的尝试注释掉了我尝试过的内容。有聪明人知道答案吗?

我希望能够在节点上调用 .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

【问题讨论】:

  • 您在问题中提到的可能重复项是什么?顺便说一句:任何享有一定声誉的人都可以关闭问题,而不仅仅是版主。
  • @Mathias:我已经修改了问题文本以提供相关文章,但它们不适用于 MSXML2.SelectNodes。
  • 为什么选择文本作为节点,请尝试从选择器中删除“()”,即"/a/b/text()""/a/b/text"
  • 我试过删除 (),它是注释掉的行之一,不起作用。至于为什么选择作为节点,答案是我想从用户那里传递一个 Xpath 表达式并且不需要特殊处理。一切都应该能够适应节点,请参阅 IXMLDOMText 以获取文本节点。 SelectNodes() 需要多态性,并且一切都返回一个节点。

标签: xml vba xpath


【解决方案1】:
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

【讨论】:

  • 好吧,我已经将其作为部分解决方案,请参阅 TestCID。我想我需要加强这个问题。我希望能够在节点上调用 .xml 方法并获取属性的值,目前这样做是提供键和值对而不仅仅是值。
  • 抱歉 - 您的问题中的文字可能过多,跳过 Debug.Assert 并仅说明每一行的结果会很有用,否则不可能只阅读问题并弄清楚什么工作或不工作。不过,我仍然不清楚这里的问题是什么。
  • Debug.Assert 非常清楚地说明了预期的结果,这确实是 Debug.Assert 的目的。一个是打开 Excel VBA 并粘贴代码。抱歉,如果您只是从文本中读取代码。
  • 几年前我广泛使用了这个。您不能使用 .xml 属性来获取值。您可以使用 SelectSingleNode 获取 IXmlAttribute - 然后在程序中使用该对象引用。如果您正在迭代混合对象类型,则必须进行类型检查,即 if TypeName(node) = "XmlAttribute" then ...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-30
  • 2011-05-30
  • 1970-01-01
  • 2017-05-18
相关资源
最近更新 更多