Удаление дочернего узла XML с помощью VBA

Это код VBA, который я сейчас использую для импорта XML в свою книгу (который работает отлично):

Sub matomoImportXML()

Dim matomoXML As Workbook

Application.DisplayAlerts = False
Set matomoXML = Workbooks.OpenXML(FileName:=matomo_xml, LoadOption:=xlXmlLoadImportToList)
Application.DisplayAlerts = True

matomoXML.Sheets(1).UsedRange.Copy report.Sheets(matomo_data).Range("A1")
matomoXML.Close False

End Sub

А это пример XML-файла:

<?xml version = "1.0" encoding = "UTF-8"?>
<result>
    <row>
        <label>lp_total_pageviews=1 - my-website.com/please-verify-you-age-to-enter/ - Others</label>
        <nb_uniq_events_eventaction>118</nb_uniq_events_eventaction>
        <nb_uniq_events_eventcategory>118</nb_uniq_events_eventcategory>
        <nb_uniq_corehome_visitip>118</nb_uniq_corehome_visitip>
        <level>3</level>
        <Events_EventAction>lp_total_pageviews=1</Events_EventAction>
        <Events_EventCategory>my-website.com%2Fplease-verify-you-age-to-enter%2F</Events_EventCategory>
        <is_summary>1</is_summary>
        <CoreHome_VisitIp>Others</CoreHome_VisitIp>
    </row>
    <row>
        <label>lp_total_clicks=1 - my-website.com/please-verify-you-age-to-enter-rs/ - xxxx:1009:b00a:6fd8:d937:5eb2:7563:de56</label>
        <nb_uniq_events_eventaction>3</nb_uniq_events_eventaction>
        <nb_uniq_events_eventcategory>3</nb_uniq_events_eventcategory>
        <nb_uniq_corehome_visitip>3</nb_uniq_corehome_visitip>
        <level>3</level>
        <Events_EventAction>lp_total_clicks=1</Events_EventAction>
        <Events_EventCategory>my-website.com%2Fplease-verify-you-age-to-enter-rs%2F</Events_EventCategory>
        <CoreHome_VisitIp>XXXX:1009:b00a:6fd8:d937:5eb2:7563:de56</CoreHome_VisitIp>
    </row>
</result>

Теперь, прежде чем импортировать и скопировать его в свою книгу, мне нужно просмотреть XML и полностью удалить дочерний узел <is_summary>некоторое значение</is_summary> (если он существует).

Я пробовал несколько решений, которые нашел в Интернете, но пока безуспешно.

Любая помощь будет принята с благодарностью!

Этого легко добиться с помощью преобразования XSLT. Вы открыты для этого?

Yitzhak Khabinsky 21.05.2024 13:52

@YitzhakKhabinsky Конечно, все работает. Но не могу ли я просто изменить XML перед его импортом?

NicoF 21.05.2024 13:55
Преобразование HTML-таблицы в профессиональный документ Excel
Преобразование HTML-таблицы в профессиональный документ Excel
Это самый простой способ создания Excel из HTML-таблицы.
Импорт excel в laravel в базу данных
Импорт excel в laravel в базу данных
Здравствуйте, дорогой читатель, в этой статье я расскажу практическим и быстрым способом, как импортировать файл Excel в вашу базу данных с помощью...
1
2
114
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вам необходимо предварительно обработать XML-файл с помощью преобразования XSLT перед его загрузкой/импортом.

В приведенном ниже XSLT используется так называемый шаблон преобразования идентичности.

Одна единственная строка <xsl:template match = "is_summary"/> предотвращает появление этого XML-элемента в выходном XML-файле.

XSLT

<?xml version='1.0'?>
<xsl:stylesheet version = "1.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
   <xsl:output method = "xml" omit-xml-declaration = "no"
               encoding = "UTF-8" indent = "yes"/>
   <xsl:strip-space elements = "*"/>

   <!--Identity transform-->
   <xsl:template match = "@*|node()">
      <xsl:copy>
         <xsl:apply-templates select = "@*|node()"/>
      </xsl:copy>
   </xsl:template>

   <xsl:template match = "is_summary"/>
</xsl:stylesheet>

ВБА

Private Sub Transform(sourceFile, stylesheetFile, resultFile)

    Dim source As New MSXML2.DOMDocument60
    Dim stylesheet As New MSXML2.DOMDocument60
    Dim result As New MSXML2.DOMDocument60

    ' Load data.
    source.async = False
    source.Load sourceFile

    ' Load style sheet.
    stylesheet.async = False
    stylesheet.Load stylesheetFile

    If (source.parseError.ErrorCode <> 0) Then
       MsgBox ("Error loading source document: " & source.parseError.reason)
    Else
        If (stylesheet.parseError.ErrorCode <> 0) Then
            MsgBox ("Error loading stylesheet document: " & stylesheet.parseError.reason)
        Else
            ' Do the transform.
            source.transformNodeToObject stylesheet, result
            result.Save resultFile
        End If
    End If

End Sub

XSLT кажется новой страной для многих программистов, использующих только xmldom, поэтому очень приятно увидеть здесь хороший пример +:)

T.M. 21.05.2024 21:40

А XSLT доступен начиная с 1999 года, то есть четверть века. 😢

Yitzhak Khabinsky 21.05.2024 21:44
Ответ принят как подходящий

Пожалуйста, попробуйте следующий способ:

Sub testRemoveXMLNode()
   'it needs a reference to 'Microsoft XML, v6.0'
   Dim xmlPath As String, XDoc As MSXML2.DOMDocument60, n As MSXML2.IXMLDOMNode
   
   xmlPath = "Your XML file full name" 'use the real name
   Set XDoc = New MSXML2.DOMDocument60
   XDoc.Load (xmlPath)
   Debug.Print XDoc.XML & vbCrLf & "___"
    Dim strTag As String: strTag = "is_summary"
    
    For Each n In XDoc.DocumentElement.ChildNodes
        recursiveTagSrc n, strTag
    Next n

    Debug.Print XDoc.XML 'just to see the result
    'it can be saved as another XML document or overwrite the original XML file (xDoc.Save xmlPath)...
End Sub
Sub recursiveTagSrc(n As MSXML2.IXMLDOMNode, strTag As String)
    Dim Nd As MSXML2.IXMLDOMNode
    If n.HasChildNodes Then
        For Each Nd In n.ChildNodes
            If Nd.HasChildNodes Then recursiveTagSrc Nd, strTag
            If Nd.nodeName = strTag Then
                n.RemoveChild Nd
            End If
        Next Nd
    End If
End Sub

Привет, @faneDuru. Спасибо! Кажется, это работает из коробки. Если вы не возражаете, один дополнительный вопрос (простите мое невежество — я новичок в VBA). Вы прокомментировали: «Он может быть сохранен как другой XML-документ». Могу ли я ПЕРЕЗАПИСАТЬ существующий XML на основе ваших изменений, чтобы моя функция импорта, которая запускается следующей, получала обновленный XML?

NicoF 21.05.2024 18:24

Конечно вы можете . Я только хотел, чтобы ты это увидел, прежде чем делать это. Чтобы быть уверенным, что оно работает так, как вам нужно.

FaneDuru 21.05.2024 19:41

@Faneduru Думаю, вы могли бы убрать скобки в XDoc.Load (xmlPath), потому что оценка не требуется. К вашему сведению, опубликована альтернатива путем удаления найденных узлов в списке узлов.

T.M. 21.05.2024 21:36

Следующая функция демонстрирует альтернативу XMLDOM рабочему решению @FaneDuru.

  • извлечение списка узлов, включающего только соответствующие узлы, с помощью простого выражения XPath (здесь: «//is_summary», где двойные косые черты указывают на поиск на любом уровне иерархии - см. раздел b) и путем
  • удаление этих результатов напрямую с помощью метода RemoveChild (см. раздел c)

вместо того, чтобы проходить все узлы один за другим, тем самым сводя к минимуму проверки, удалять или нет.

Примечание:

Действительный подход @Ицхака-Хабинского для удаления использует не методы XMLDOM, а XSLT, язык специального назначения, предназначенный для преобразования исходных XML-файлов (в данном случае путем удаления узлов посредством преобразования идентичности) в соответствии с различными критериями.

Function cleanXML(ByVal xmlFilename As String, _
    ByVal DELNODE As String) _
    As MSXML2.DOMDocument60
'a) load src
    Dim xDoc As MSXML2.DOMDocument60
    Set xDoc = New MSXML2.DOMDocument60
    xDoc.async = False
    xDoc.Load xmlFilename
'b) get nodelist
    Dim nodes As MSXML2.IXMLDOMNodeList
    Set nodes = xDoc.SelectNodes("//" & DELNODE)
'c) remove each node nodelist
    Dim node As MSXML2.IXMLDOMNode
    For Each node In nodes
        node.ParentNode.RemoveChild node
    Next
'd) return xml as object
    Set cleanXML = xDoc
End Function

Пример звонка

Sub ExampleCall()
    Dim t As Double: t = Timer
'0) initial definitions
    Const DELNODE As String = "is_summary"
    Dim fn As String: fn = ThisWorkbook.Path & "\xml\" & "Whatever.xml"
'1) get result with removed DELNODEs
    Dim result As MSXML2.DOMDocument60
    Set result = cleanXML(fn, DELNODE)
'2) display result
    Debug.Print result.xml
    Debug.Print Format(Timer - t, "0.0 secs needed")
    
''3) save result to any destination filename
'    Dim destFn As String: destFn = fn  ' e.g. overwrite src
'    result.Save destFn
End Sub

Другие вопросы по теме