【发布时间】: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() 需要多态性,并且一切都返回一个节点。