Excel VBA XML: выбор заданного значения тега в цикле, когда узлы имеют атрибуты - PullRequest
0 голосов
/ 08 апреля 2019

Я просмотрел много разных решений и попробовал несколько строк кода, но не смог добиться успеха на моем примере.Я пытаюсь получить текстовое значение (это нормально) узлов XML, но остается застрявшим с атрибутами узла.

Вот мой XML:

<MyList>

  <Entry Id="33">
    <Category>
      <Mycategory Style="One">
        <Rule Id="37">
          <Text>xxx123</Text>
        </Rule>  
        <Rule Id="476">
          <Text>123</Text>
        </Rule>  
      </Mycategory>

      <Mycategory Style="Two">
        <Rule Id="3756">
          <Text>xxx456</Text>
        </Rule>
        <Rule Id="734">
          <Text>456</Text>
        </Rule>
      </Mycategory>
    </Category>

  <Entry Id="821">
    <Category>
      <Mycategory Style="One">
        <Rule Id="538">
          <Text>xxxaaa</Text>
        </Rule>
        <Rule Id="366">
          <Text>aaa</Text>
        </Rule>    
      </Mycategory>

      <Mycategory Style="Two">
        <Rule Id="894">
          <Text>xxxbbb</Text>
        </Rule>
        <Rule Id="921">
          <Text>bbb</Text>
        </Rule>
      </Mycategory>
    </Category>

(etc. until the end of the XML)

Я хочу следующий Excelс VBA:

Style One |Стиль Два
ххх123 |xxx456
xxxaaa |xxxbbb

In extenso: только текстовое значение (начинающееся с «xxx») первого узла <<Rule>.Второй узел <<Rule> можно игнорировать.

Все значения ID = "" являются случайными.

Мой код VBA:

Sub Parsing()

Dim mainWorkBook As Workbook
Dim XMLFileName, Sheet As String
Dim nodeList As IXMLDOMNodeList

Set mainWorkBook = Workbooks("MyExcel.xlsm")
Set oXMLFile = CreateObject("Microsoft.XMLDOM")

XMLFileName = "C:\MyData.xml"
Sheet = "Sheet1"

oXMLFile.Load (XMLFileName)
Set StyleOne_Nodes = oXMLFile.SelectSingleNode("/MyList/Entry/Category/Mycategory[@name='One']/Rule/Text").Text
Set StyleTwo_Nodes = oXMLFile.SelectSingleNode("/MyList/Entry/Category/Mycategory[@name='Two']/Rule/Text").Text

For i = 0 To (StyleOne_Nodes.Length - 1)
    mainWorkBook.Sheets(Sheet).Range("A" & i + 2).Value = StyleOne_Nodes(i).NodeValue
    mainWorkBook.Sheets(Sheet).Range("B" & i + 2).Value = StyleTwo_Nodes(i).NodeValue
Next

End Sub

Код не включенSelectSingleNode.Я пробовал разные способы (SelectNodes, SelectSingleNode, getElementsByTagName), но не смог получить мои текстовые значения (например, xxx123 и xxx456) в ячейках Excel.
Кроме того, неясны циклы справа '<' Rule>.
Как я могу получить только значение '<' Text> первого '<' Правила> только (с 'xxx'; игнорируя второе '<' Правило>), цикл после цикла (= '<' Entry> after '<'Entry>)?

Спасибо за помощь заранее.

Ответы [ 2 ]

1 голос
/ 08 апреля 2019

Игнорирование того факта, что образец XML недействителен, что-то вроде следующего является одним из способов решения проблемы. Это будет группировать по стилю:

Sub Parsing()
   Dim XMLFileName As String
   Dim oXMLFile As DOMDocument60
   Dim StyleOne_Nodes As IXMLDOMNodeList
   Dim StyleTwo_Nodes As IXMLDOMNodeList
   Dim n As IXMLDOMNode
   Dim c As IXMLDOMNode

   XMLFileName = "C:\temp\mydata.xml"
   Set oXMLFile = New DOMDocument60
   oXMLFile.Load XMLFileName

   Set StyleOne_Nodes = oXMLFile.selectNodes("/MyList/Entry/Category/Mycategory[@Style='One']")
   Set StyleTwo_Nodes = oXMLFile.selectNodes("/MyList/Entry/Category/Mycategory[@Style='Two']")

   If Not StyleOne_Nodes Is Nothing Then
      For Each n In StyleOne_Nodes
         Set c = n.selectSingleNode("Rule/Text")
         Debug.Print c.Text
      Next
   End If

   If Not StyleTwo_Nodes Is Nothing Then
      For Each n In StyleTwo_Nodes
         Set c = n.selectSingleNode("Rule/Text")
         Debug.Print c.Text
      Next
   End If
End Sub

Для простоты я сосредоточился исключительно на чтении XML-файла, поэтому я удалил весь код Excel. Это должно быть просто для вас, чтобы положить его обратно.

EDIT:

Основываясь на дополнительном запросе от ОП и для ясности, я представляю оригинальную логику QHarr. Это будет печатать строка за строкой:

Sub Parsing2()
   Dim XMLFileName As String
   Dim oXMLFile As DOMDocument60
   Dim All_Nodes As IXMLDOMNodeList
   Dim n As IXMLDOMNode

   XMLFileName = "C:\temp\mydata.xml"
   Set oXMLFile = New DOMDocument60
   oXMLFile.Load XMLFileName

   Set All_Nodes = oXMLFile.selectNodes("//Mycategory/*[1]/Text")

   If Not All_Nodes Is Nothing Then
      For Each n In All_Nodes
         Debug.Print n.Text
      Next
   End If
End Sub
0 голосов
/ 08 апреля 2019

Я ожидаю, что xpath похож на

//Mycategory/*[1]/Text

Мы можем выделить стили, если это необходимо, используя:

//Mycategory[@Style='One']/*[1]/Text

И

//Mycategory[@Style='Two']/*[1]/Text

Моя версия XML исправлена ​​в конце. Создает ожидаемый текст узла.

Option Explicit
Public Sub test()
    Dim xmlDoc As Object
    Set xmlDoc = CreateObject("MSXML2.DOMDocument")
    With xmlDoc
        .validateOnParse = True
        .setProperty "SelectionLanguage", "XPath"
        .async = False

        If Not .Load("C:\Users\User\Desktop\Test.xml") Then
            Err.Raise .parseError.ErrorCode, , .parseError.reason
        End If
    End With
    Dim node As IXMLDOMElement
    '    For Each node In xmlDoc.SelectNodes("//Mycategory/*[1]/Text")
    '        Debug.Print node.Text
    '    Next
    For Each node In xmlDoc.SelectNodes("//Mycategory[@Style='One']/*[1]/Text")
       Debug.Print node.Text
    Next
    For Each node In xmlDoc.SelectNodes("//Mycategory[@Style='Two']/*[1]/Text")
        Debug.Print node.Text
    Next
End Sub

Изменить для печати значения стиля перед каждой группой:

Option Explicit
Public Sub test()
    Dim xmlDoc As Object
    Set xmlDoc = CreateObject("MSXML2.DOMDocument")
    With xmlDoc
        .validateOnParse = True
        .setProperty "SelectionLanguage", "XPath"
        .async = False

        If Not .Load("C:\Users\User\Desktop\Test.xml") Then
            Err.Raise .parseError.ErrorCode, , .parseError.reason
        End If
    End With
    Dim node As Object, i As Long, xpath As Variant, j As Long
    For Each xpath In Array("//Mycategory[@Style='One']/*[1]/Text", "//Mycategory[@Style='Two']/*[1]/Text")
        j = 0
        For Each node In xmlDoc.SelectNodes(xpath)
            For i = 0 To node.ParentNode.ParentNode.Attributes.Length - 1
                If node.ParentNode.ParentNode.Attributes(i).nodeName = "Style" And j = 0 Then
                   Debug.Print node.ParentNode.ParentNode.Attributes(i).nodeName & Chr$(32) & node.ParentNode.ParentNode.Attributes(i).NodeValue
                End If
            Next
            Debug.Print node.Text
            j = j + 1
        Next
    Next
End Sub

С действительной структурой xml:

<MyList>
    <Entry Id="33">
        <Category>
            <Mycategory Style="One">
                <Rule Id="37">
                    <Text>xxx123</Text>
                </Rule>
                <Rule Id="476">
                    <Text>123</Text>
                </Rule>
            </Mycategory>
            <Mycategory Style="Two">
                <Rule Id="3756">
                    <Text>xxx456</Text>
                </Rule>
                <Rule Id="734">
                    <Text>456</Text>
                </Rule>
            </Mycategory>
        </Category>
    </Entry>
    <Entry Id="821">
        <Category>
            <Mycategory Style="One">
                <Rule Id="538">
                    <Text>xxxaaa</Text>
                </Rule>
                <Rule Id="366">
                    <Text>aaa</Text>
                </Rule>
            </Mycategory>
            <Mycategory Style="Two">
                <Rule Id="894">
                    <Text>xxxbbb</Text>
                </Rule>
                <Rule Id="921">
                    <Text>bbb</Text>
                </Rule>
            </Mycategory>
        </Category>
    </Entry>
</MyList>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...