Почему Object.Selectnodes (XPath) получает значение 1-го узла, если предыдущий узел пуст - PullRequest
1 голос
/ 03 мая 2019

Почему Object.SelectNodes(XPath) получает в качестве значения 1-го узла значение второго узла, если прежний узел (реальное значение 1-го узла) пуст.Пример ниже:

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

Почему «Ничего» не на первом месте в результате?Как этого достичь?Если родительский узел не включает 1-й дочерний узел, 1-я итерация пропускается.Спасибо.

1 Ответ

2 голосов
/ 03 мая 2019

Перечисление в 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-структуры с помощью рекурсивных вызовов; рабочую функцию можно найти на Синтаксический анализ XML с использованием 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

...