2015-04-18 4 views
0

Как создать цикл для узла, который не всегда может быть частью его родителя - при анализе данных для других узлов?VBA XML DOM Поиск элемента, который может не всегда существовать

Предположим, у вас есть очень большой файл с несколькими из этих элементов, но для простоты мы будем использовать этот XML (обратите внимание на первую «книгу идентификатор» не имеет узел, мы хотим, поэтому наш цикл уже не удается):

<?xml version="1.0"?> 
<catalog> 
<book id="Adventure" ISBN="00113" version="13"> 
    <author>Ralls, Kim</author> 
    <title>XML Developer's Guide</title> 
    <price>44.95</price> 
    <misc> 
     <editor id="9B"> 
      <editorBrand>Partial Edit</editorBrand> 
      <editorEmphasis>Minimal</editorEmphasis> 
     </editor> 
    </misc> 
</book> 
<book id="Adventure" ISBN="00114" version="14"> 
    <author>Ralls, Kim</author> 
    <title>Midnight Rain</title> 
    <price>5.95</price> 
    <misc> 
     <Publisher id="5691"> 
      <PublisherLocation>Los Angeles</PublisherLocation> 
     </Publisher> 
     <PublishedAuthor id="Ralls"> 
      <StoreLocation>Store A/8</StoreLocation> 
      <seriesTitle>AAA</seriesTitle> 
       <store id="8"> 
        <copies>26</copies> 
       </store> 
    </misc> 
</book> 
<book id="Adventure" ISBN="00115" version="14"> 
    <author>Ralls, Kim</author> 
    <title>Mist</title> 
    <price>15.95</price> 
    <misc> 
     <Publisher id="8101"> 
      <PublisherLocation>New Mexico</PublisherLocation> 
     </Publisher> 
     <PublishedAuthor id="Ralls"> 
      <StoreLocation>Market C/13</StoreLocation> 
      <seriesTitle>BBB</seriesTitle> 
       <store id="9"> 
        <copies>150</copies> 
       </store> 
       <store id="13"> 
        <copies>60</copies> 
       </store> 
     </PublishedAuthor> 
    </misc> 
</book> 
<book id="Mystery" ISBN="00116" version="13"> 
    <author>Bill, Simmons</author> 
    <title>NBA Insider</title> 
    <price>16.99</price> 
    <misc> 
     <editor id="11N"> 
      <editorBrand>Full Edit</editorBrand> 
      <editorEmphasis>Full</editorEmphasis> 
     </editor> 
    </misc> 
</book> 
</catalog> 

Наш VBA Код:

Sub mySub() 

Dim XMLFile As Variant 
Dim seriesTitle As Variant 
Dim series As String, Author As String, Title As String, StoreLocation As String 
Dim ISBN As String, copies As String, storelc As String 
Dim seriesArray() As String, AuthorArray() As String, BookTypeArray() As String, TitleArray() As String 
Dim StoreLocationArray() As String, ISBNArray() As String, copiesArray() As String 
Dim i As Long, x As Long, j As Long, pn As Object, loc As Object, arr, ln As String, loc2 As Object 

Dim mainWorkBook As Workbook 
Dim n As IXMLDOMNode 
Set mainWorkBook = ActiveWorkbook 
Set XMLFile = CreateObject("Microsoft.XMLDOM") 
XMLFile.Load ("C:\Books.xml") 
XMLFile.setProperty "SelectionLanguage", "XPath" 

x = 1 
j = 0 

Set seriesTitle = XMLFile.SelectNodes("/catalog/book/misc/PublishedAuthor/seriesTitle") 
For i = 0 To (seriesTitle.Length - 1) 

series = seriesTitle(i).Text 
storelc = seriesTitle(i).SelectSingleNode("store/copies").Text 

If series = "AAA" Or series = "BBB" Then 

    Set pn = seriesTitle(i).ParentNode 
    StoreLocation = pn.getElementsByTagName("StoreLocation").Item(0).nodeTypedValue 
    Author = pn.ParentNode.ParentNode.getElementsByTagName("author").Item(0).nodeTypedValue 
    Title = pn.ParentNode.ParentNode.getElementsByTagName("title").Item(0).nodeTypedValue 
    ISBN = pn.ParentNode.ParentNode.getAttribute("ISBN") 

    Set loc = pn.SelectSingleNode("seriesTitle/store[@id='" & storelc & "']/copies") 
    If loc Is Nothing Then 
     arr = Split(storelc, "/") 
     ln = Trim(arr(UBound(arr))) 
     Set loc = pn.SelectSingleNode("seriesTitle/store[@id='" & ln & "']/copies") 
    End If 

    If Not loc Is Nothing Then 
     copies = loc.Text 
    Else 
     copies = "?" 
    End If 

    AddValue seriesArray, series 
    AddValue AuthorArray, Author 
    AddValue TitleArray, Title 
    AddValue StoreLocationArray, StoreLocation 
    AddValue ISBNArray, ISBN 
    AddValue copiesArray, copies 

    j = j + 1 
    x = x + 1 
End If 
Next 

Range("A3").Resize(j, 1).Value = WorksheetFunction.Transpose(AuthorArray) 
Range("B3").Resize(j, 1).Value = WorksheetFunction.Transpose(TitleArray) 
Range("C3").Resize(j, 1).Value = WorksheetFunction.Transpose(ISBNArray) 
Range("D3").Resize(j, 1).Value = WorksheetFunction.Transpose(seriesArray) 
Range("E3").Resize(j, 1).Value = WorksheetFunction.Transpose(StoreLocationArray) 
Range("F3").Resize(j, 1).Value = WorksheetFunction.Transpose(copiesArray) 

End Sub 

'Utility method - resize an array as needed, and add a new value 

Sub AddValue(arr, v) 
    Dim i As Long 
    i = -1 
    On Error Resume Next 
    i = UBound(arr) + 1 
    On Error GoTo 0 
    If i = -1 Then i = 0 
    ReDim Preserve arr(0 To i) 
    arr(i) = v 
End Sub 

Моя цель состоит в том, чтобы искать "seriesTitle". Поэтому я специально создаю цикл For, который ищет длину найденных элементов, а затем проанализирует «seriesTitle» вместе с ISBN, StoreLocation, Author, Book Title и копиями.

  1. Если существует серия Title - это версия 14, то - я хочу распечатать серию Title, ISBN, StoreLocation, Author, Название книги и копии.
  2. Если seriesTitle НЕ существует - это версия 13, тогда - я хочу только напечатать ISBN, Автор и Название книги.

Но проблема в том, что для каждого «идентификатора книги», который существует, необязательно есть «seriesTitle» - единственное отношение, которое мы можем сделать, это то, что когда «version = 13» нет серийного названия.

  • Как бы вы пропустили весь документ, если у вас нет объекта для создания поиска по циклу For? И когда «seriesTitle» не существует, как бы вы продолжаете добавлять элементы в массив ISBN, Author и Book Title?

Благодарим вас за то, что вы преподаете мне с любыми полезными комментариями и предложениями!

+1

Почему бы не начать * с набором узлов и не перечеркнуть их? Казалось бы, это самый простой способ приблизиться к этому: вы можете проверить атрибут версии книги и работать с этим, когда решаете, искать ли узел seriesTitle. –

+0

@TimWilliams У меня была попытка создать цикл For, который искал книги, а затем другой цикл For внутри, который будет фильтровать нужные мне узлы, но в итоге я буквально распечатал цикл цикла x количества книг. – NRH

ответ

1

В соответствии мой комментарий, кажется, что вы бы лучше просто цикл по всем <book> элементов и чтение их дочерние узлы для требуемых значений, а не перемещаясь вверх и вниз Дерево DOM довольно много.

Sub Tester() 

Dim d As New MSXML2.DOMDocument 
Dim bks As MSXML2.IXMLDOMNodeList 
Dim bk As Object 
Dim cat As Object, sertitle 
Dim isbn, storeLoc, auth, seriesTitle, vsn, copies, title 

    d.setProperty "SelectionLanguage", "XPath" 
    d.LoadXML Sheet1.Range("A1").Value 

    Set bks = d.SelectNodes("/catalog/book") 
    For Each bk In bks 

     vsn = bk.getAttribute("version") 
     isbn = bk.getAttribute("ISBN") 
     title = GetTextSafely(bk, "title") 
     storeLoc = GetTextSafely(bk, "misc/PublishedAuthor/StoreLocation") 
     seriesTitle = GetTextSafely(bk, "misc/PublishedAuthor/seriesTitle") 
     auth = GetTextSafely(bk, "author") 

     copies = "??" ' I'm unclear exactly what you're doing here.... 

     Debug.Print vsn, isbn, storeLoc, seriesTitle, auth, title, copies 

    Next bk 

End Sub 

'utility function: get a node's value if it exists 
Function GetTextSafely(el As Object, path As String) 
    Dim nd, rv 
    Set nd = el.SelectSingleNode(path) 
    If Not nd Is Nothing Then rv = nd.nodeTypedValue 
    GetTextSafely = rv 
End Function 
+0

Есть ли разница между 'For Each bk In bks' и say' For i = 0 To (bks.length - 1) '? Является ли еще один идеал в зависимости от условий? – NRH

+1

Они оба делают то же самое. Иногда, если вам нужен счетчик внутри цикла, более удобно использовать версию «For i = ...» –

1

Во-первых, ваш xml содержит ошибку. Вам не хватает закрывающего тега. Смотрите новый XML ниже

<?xml version="1.0"?> 
 
<catalog> 
 
    <book id="Adventure" ISBN="00113" version="13"> 
 
    <author>Ralls, Kim</author> 
 
    <title>XML Developer's Guide</title> 
 
    <price>44.95</price> 
 
    <misc> 
 
     <editor id="9B"> 
 
     <editorBrand>Partial Edit</editorBrand> 
 
     <editorEmphasis>Minimal</editorEmphasis> 
 
     </editor> 
 
    </misc> 
 
    </book> 
 
    <book id="Adventure" ISBN="00114" version="14"> 
 
    <author>Ralls, Kim</author> 
 
    <title>Midnight Rain</title> 
 
    <price>5.95</price> 
 
    <misc> 
 
     <Publisher id="5691"> 
 
     <PublisherLocation>Los Angeles</PublisherLocation> 
 
     </Publisher> 
 
     <PublishedAuthor id="Ralls"> 
 
     <StoreLocation>Store A/8</StoreLocation> 
 
     <seriesTitle>AAA</seriesTitle> 
 
     <store id="8"> 
 
      <copies>26</copies> 
 
     </store> 
 
     </PublishedAuthor> 
 
     </misc> 
 
    </book> 
 
    <book id="Adventure" ISBN="00115" version="14"> 
 
    <author>Ralls, Kim</author> 
 
    <title>Mist</title> 
 
    <price>15.95</price> 
 
    <misc> 
 
     <Publisher id="8101"> 
 
     <PublisherLocation>New Mexico</PublisherLocation> 
 
     </Publisher> 
 
     <PublishedAuthor id="Ralls"> 
 
     <StoreLocation>Market C/13</StoreLocation> 
 
     <seriesTitle>BBB</seriesTitle> 
 
     <store id="9"> 
 
      <copies>150</copies> 
 
     </store> 
 
     <store id="13"> 
 
      <copies>60</copies> 
 
     </store> 
 
     </PublishedAuthor> 
 
    </misc> 
 
    </book> 
 
    <book id="Mystery" ISBN="00116" version="13"> 
 
    <author>Bill, Simmons</author> 
 
    <title>NBA Insider</title> 
 
    <price>16.99</price> 
 
    <misc> 
 
     <editor id="11N"> 
 
     <editorBrand>Full Edit</editorBrand> 
 
     <editorEmphasis>Full</editorEmphasis> 
 
     </editor> 
 
    </misc> 
 
    </book> 
 
</catalog>​

Если когда-нибудь у вас есть многофункциональный объект уровня и уровней отсутствует, вы должны искать на одном уровне, в то время. Каждая книга имеет тег «misc». Поэтому вы сначала должны перечислить книги «misc». Затем проверьте, существует ли ребенок.

Set misc = XMLFile.SelectNodes("catalog/book/misc") 
 
For a = 0 To (misc.Length - 1) 
 
    Set publishedAuthor = XMLFile.SelectNodes("/catalog/book/misc/PublishedAuthor/seriesTitle") 
 
    If Not publishedAuthor Is Nothing Then 
 

 
    End If 
 
Next a

+0

Спасибо.Но если в файле есть сотни , разве это не очень эффективно? Потому что тогда мне придется искать такой широкий спектр и продолжать пытаться его уточнить. – NRH

+0

У вас есть фильтр узлов, которые вы ищете в разное. Все, что вам действительно нужно, это узлы с PublisherAuthors. Правильно? Использование XML.Linq может помочь выполнить фильтрацию. Но вы все равно должны придумать правильный набор фильтров. – jdweng

+0

@jweng Я никогда не слышал о XML.Linq, но я это рассмотрю. Спасибо. – NRH

Смежные вопросы