如果前一个节点为空,为什么Object.Selectnodes(XPath)获得第一个节点值

时间:2019-05-02 21:41:42

标签: excel xml vba xmldom

如果先前的节点(实际的第一节点值)为空,为什么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”不放在首位?如何达到?如果父节点不包括第一子节点,则将省略第一迭代。 谢谢。

1 个答案:

答案 0 :(得分:2)

XMLDOM中的枚举不同于XPath

XMLDOM 语法将nodes中的NodeList枚举为基于零的项,即从0开始,而标识子节点的XPath表达式1开始(例如,调用名字项"//name[1]")。在将2作为最后一个项目索引而不是在示例代码中从.Item(0)循环到.Item(1)时,这是错误的。您可以通过节点列表的.Length方法获得找到的项目结果数(2减1表示最后一个索引号为1,因此名称节点系列为0到1)。

此外,建议参考 MSXML2 6.0版 MSXML2.DOMDocument指的是仅出于兼容性原因而使用的最后一个稳定版本3.0)

进一步提示假设您要遍历XML文档中的所有(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)来列举最多三个项目。

附加链接

通过递归调用分析XML结构;可以在Parse XML using XMLDOM找到工作功能。

代码示例

    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