如果前一个节点为空,为什么 Object.Selectnodes(XPath) 获取第一个节点值
Why Object.Selectnodes(XPath) gets 1st node value if former node is empty
如果前一个节点(真正的第一个节点值)为空,为什么Object.SelectNodes(XPath)
得到第一个节点值第二个节点值。
示例如下:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<Document>
<person>
</person>
<person>
<name>Peter</name>
</person>
</Document>
VBA代码:
Dim j as Integer
Dim FileToOpen as Variant
FileToOpen = Application.GetOpenFilename(Filefilter:="XML Files (*.xml), *.xml", _
Title:="Choose XML document ", MultiSelect:=False)
Set XDoc = CreateObject("MSXML2.DOMDocument")
XDoc.async = False: XDoc.validateOnParse = False
XDoc.Load FileToOpen
For j = 1 To 2
Set tofields = XDoc.SelectNodes("//Document/person/name")
If Not (tofields.Item(j)) Is Nothing Then
Debug.Print tofields.Item(j).Text
Else
Debug.Print "Nothing"
End If
Next j
结果:
Peter
Nothing
为什么 "Nothing" 不在结果的第一位?如何达到那个?如果父节点不包含第一个子节点,则省略第一次迭代。
谢谢。
XMLDOM 中的枚举不同于 XPath
XMLDOM 语法将 nodes
枚举为 零基 项 NodeList
,即从 0
开始,而 XPath 表达式 标识子节点从 1
开始(例如调用名字项 "//name[1]"
)。在将 2
视为最后一个项目索引而不是在示例代码中从 .Item(0)
循环到 .Item(1)
时,这是错误的。您可以通过节点列表的 .Length
方法获得找到的项目数结果(2 减 1 结果为 1 作为最后一个索引号,因此给出 0 到 1 的名称节点系列)。
另外建议参考MSXML2 version 6.0 (MSXML2.DOMDocument
指最后稳定版3.0仅用于兼容性原因).
进一步的提示假设你想遍历 XML 文档中的所有 persons(OP 中的节点列表只提供一个项目,因为 name
节点只存在一次):
- 表达式
xDoc.SelectNodes("//Document/person")
或//person
将在给定节点结构内的任何层次结构级别搜索定义的节点集。因此,在您的情况下明确使用 Set toFields = xDoc.DocumentElement.SelectNodes("person")
会更省时。
- 以下代码示例不会显示
Nothing
情况,因为节点列表仅显示两个 name
节点 (For i = 0 To toFields.Length - 1
)。 为了检查您最初的尝试,您可以通过专心更改为 For i = 0 To toFields.Length
(即 0 to 2
)来枚举最多三个项目。
补充link
通过递归调用分析你的XML结构;可以在 .
找到工作函数
代码示例
Dim xDoc As Object, toFields As Object
Dim myName As String, i As Long
Set xDoc = CreateObject("MSXML2.DOMDocument.6.0") ' recommended version 6.0 (if late bound MSXML2)
xDoc.async = False: xDoc.validateOnParse = False
' ...
Set toFields = xDoc.DocumentElement.SelectNodes("person")
For i = 0 To toFields.Length - 1
If Not toFields.Item(i) Is Nothing Then
If toFields.Item(i).HasChildNodes Then
myName = toFields.Item(i).SelectSingleNode("name").Text
Debug.Print i, IIf(Len(Trim(myName)) = 0, "**Empty name", myName)
Else
Debug.Print i, "**No name node**"
End If
Else
Debug.Print i, "**Nothing**" ' shouldn't be needed from 0 to .Length-1 items :-)
End If
Next i
如果前一个节点(真正的第一个节点值)为空,为什么Object.SelectNodes(XPath)
得到第一个节点值第二个节点值。
示例如下:
XML:
<?xml version="1.0" encoding="UTF-8"?>
<Document>
<person>
</person>
<person>
<name>Peter</name>
</person>
</Document>
VBA代码:
Dim j as Integer
Dim FileToOpen as Variant
FileToOpen = Application.GetOpenFilename(Filefilter:="XML Files (*.xml), *.xml", _
Title:="Choose XML document ", MultiSelect:=False)
Set XDoc = CreateObject("MSXML2.DOMDocument")
XDoc.async = False: XDoc.validateOnParse = False
XDoc.Load FileToOpen
For j = 1 To 2
Set tofields = XDoc.SelectNodes("//Document/person/name")
If Not (tofields.Item(j)) Is Nothing Then
Debug.Print tofields.Item(j).Text
Else
Debug.Print "Nothing"
End If
Next j
结果:
Peter
Nothing
为什么 "Nothing" 不在结果的第一位?如何达到那个?如果父节点不包含第一个子节点,则省略第一次迭代。 谢谢。
XMLDOM 中的枚举不同于 XPath
XMLDOM 语法将 nodes
枚举为 零基 项 NodeList
,即从 0
开始,而 XPath 表达式 标识子节点从 1
开始(例如调用名字项 "//name[1]"
)。在将 2
视为最后一个项目索引而不是在示例代码中从 .Item(0)
循环到 .Item(1)
时,这是错误的。您可以通过节点列表的 .Length
方法获得找到的项目数结果(2 减 1 结果为 1 作为最后一个索引号,因此给出 0 到 1 的名称节点系列)。
另外建议参考MSXML2 version 6.0 (MSXML2.DOMDocument
指最后稳定版3.0仅用于兼容性原因).
进一步的提示假设你想遍历 XML 文档中的所有 persons(OP 中的节点列表只提供一个项目,因为 name
节点只存在一次):
- 表达式
xDoc.SelectNodes("//Document/person")
或//person
将在给定节点结构内的任何层次结构级别搜索定义的节点集。因此,在您的情况下明确使用Set toFields = xDoc.DocumentElement.SelectNodes("person")
会更省时。 - 以下代码示例不会显示
Nothing
情况,因为节点列表仅显示两个name
节点 (For i = 0 To toFields.Length - 1
)。 为了检查您最初的尝试,您可以通过专心更改为For i = 0 To toFields.Length
(即0 to 2
)来枚举最多三个项目。
补充link
通过递归调用分析你的XML结构;可以在
代码示例
Dim xDoc As Object, toFields As Object
Dim myName As String, i As Long
Set xDoc = CreateObject("MSXML2.DOMDocument.6.0") ' recommended version 6.0 (if late bound MSXML2)
xDoc.async = False: xDoc.validateOnParse = False
' ...
Set toFields = xDoc.DocumentElement.SelectNodes("person")
For i = 0 To toFields.Length - 1
If Not toFields.Item(i) Is Nothing Then
If toFields.Item(i).HasChildNodes Then
myName = toFields.Item(i).SelectSingleNode("name").Text
Debug.Print i, IIf(Len(Trim(myName)) = 0, "**Empty name", myName)
Else
Debug.Print i, "**No name node**"
End If
Else
Debug.Print i, "**Nothing**" ' shouldn't be needed from 0 to .Length-1 items :-)
End If
Next i