kraemer
Goto Top

PowerShell - Suche performantere Lösung für das Verschieben von XML-Nodes

Moin,

ich habe da ein Performanceproblem beim verschieben von XML-Nodes.
Es geht um folgendes:
Ich habe eine XML-Datei. Ca. 3 Millionen Zeilen lang. Der Aufbau ist wie folgt:

<EINS>
	<ZWEI>
		<DREI>
			<VIER></VIER>
			...zigtausende <VIER></VIER>
		</DREI>
		<DREI>
			<VIER></VIER>
			...zigtausende <VIER></VIER>
		</DREI>
	</ZWEI>
</EINS>

Ich muss nun die Elemente aus Ebene 4 auf die Ebene 1 verschieben. Ebene 1 und 2 existieren in der ganzen Datei jeweils nur 4 mal. Aktuell löse ich das so, das ich die entsprechende Node clone, an Ebene 1 "appende" und dann das Ursprungselement lösche. Leider dauert das ewig. Ich kann noch nicht einmal sagen wie lange - nach 15 Minuten breche ich ab. Das ganze muss nämlich in deutlich unter 10 Minuten erledigt sein.

    $doc.SelectNodes('//VIER') | %{  
        $doc.EINS.AppendChild($_.clone()) | Out-Null
        $_.ParentNode.RemoveChild($_) | Out-Null
        }

Hat hier evtl. jemand eine Idee, wie ich das Ganze beschleunigen kann?

Gruß Krämer

Content-Key: 314827

Url: https://administrator.de/contentid/314827

Ausgedruckt am: 19.03.2024 um 02:03 Uhr

Mitglied: Friemler
Friemler 09.09.2016 um 10:10:00 Uhr
Goto Top
Moin,

ich habe nicht viel Ahnung von Powershell und .NET aber aufgrund von Erfahrungen in anderen Bereichen würde ich schrittweise die Komplexität des Befehls auflösen, wenn ein Schritt nicht hilft dann zum nächsten übergehen:

  1. Statt die Einzelschritte über Pipes miteinander zu verbinden explizit mit Variablen arbeiten.
  2. Die Einzelbefehle "atomisieren", d.h. die Liste der Vorgänge, die durch einen Einzelbefehl implizit abgearbeitet wird, "von Hand" schreiben.
  3. Einen anderen XML-Provider verwenden, z.B. ActiveX-Objekte in VBScript. .NET ist zwar sehr schick aber kein nativer Code, da existiert sicherlich Potential für Performance-Steigerung.

Grüße
Friemler
Mitglied: 129813
Lösung 129813 09.09.2016 aktualisiert um 10:37:19 Uhr
Goto Top
Hi,
you should use an XMLTextReader-Object instead, it parses XML files much faster.
https://msdn.microsoft.com/en-us/library/ff647804.aspx#scalenetchaptch09 ...

Regards
Mitglied: colinardo
Lösung colinardo 09.09.2016 aktualisiert um 11:10:11 Uhr
Goto Top
Hallo Krämer,
für sowas würde ich eher zu XSL-Transformation greifen das geht instant, habe ich hier vor kurzem schon mal gezeigt:

Grüße Uwe

p.s. Erst das Klonen und dann Entfernen ist beim Verschieben überflüssig wenn man stattdessen InserAfter() benutzt, das ist aber ähnlich langsam. Besser du greifst direkt zu XSLT
Mitglied: Kraemer
Kraemer 09.09.2016 aktualisiert um 11:55:20 Uhr
Goto Top
Hi,

Zitat von @129813:
you should use an XMLTextReader-Object instead,
Das mag durchaus ein guter Gedanke sein, wenn sonst nichts gemacht werden müsste. Ich muss aber noch Sortieren, Filtern etc. Lt. Beschreibung wäre da der Reader das falsche Mittel. Trotzdem Danke für den Tipp - habe wieder was dazu gelernt.

Gruß
Mitglied: Kraemer
Kraemer 09.09.2016 um 11:52:14 Uhr
Goto Top
Moin Uwe,


Zitat von @colinardo:
für sowas würde ich eher zu XSL-Transformation greifen das geht instant, habe ich hier vor kurzem schon mal gezeigt:
die beiden Threads hatte ich sogar verfolgt. Keine Ahnung warum ich nicht darauf gekommen bin, dass das bei meinem Problem helfen könnte. Ich muss mir das unbedingt mal genauer ansehen.

p.s. Erst das Klonen und dann Entfernen ist beim Verschieben überflüssig wenn man stattdessen InserAfter() benutzt, das ist aber ähnlich langsam. Besser du greifst direkt zu XSLT
Verstehe ich das richtig, dass ich einen vorhandenen Node mit InsertAfter auch verschieben kann? Ich hatte das bisher so verstanden, das InsertAfter nur für das Erstellen eines Nodes geeignet ist.

Gruß Krämer
Mitglied: Kraemer
Kraemer 09.09.2016 um 11:54:48 Uhr
Goto Top
Moin zusammen,

ich habe einfach mal Quer gedacht und einen ganz pragmatischen Ansatz gefunden. Leider weiß ich noch nicht 100%ig ob mein Vorgehen Nebenwirkungen haben könnte.

$doc.InnerXml=$doc.InnerXml -replace ('<\/?DREI>','')  
$doc.InnerXml=$doc.InnerXml -replace ('<\/?ZWEI>','')  

Scheint tadellos zu funktionieren und ist vor allem extrem schnell.

Grüße Krämer
Mitglied: colinardo
Lösung colinardo 09.09.2016 aktualisiert um 11:59:03 Uhr
Goto Top
Zitat von @Kraemer:
Verstehe ich das richtig, dass ich einen vorhandenen Node mit InsertAfter auch verschieben kann?
Ja. Wenn man ein vorhandenen Node angibt wird dieser verschoben, ist aber nicht merklich schneller.
$first = $xml.DocumentElement
$nodes = $xml.SelectNodes('//vier')  
foreach($node in $nodes){
    [void]$first.InsertAfter($node,$first.LastChild)
}

Ich muss aber noch Sortieren, Filtern etc.
Geht alles auch mit XSL face-smile
Mitglied: colinardo
Lösung colinardo 09.09.2016 aktualisiert um 12:02:32 Uhr
Goto Top
Zitat von @Kraemer:
Scheint tadellos zu funktionieren und ist vor allem extrem schnell.
Kann man machen wenn man immer gleiche XMLs geliefert bekommt und die Verschachtelung gleich bleibt. Ich dachte jetzt eher an unterschiedlich verschachtelte XMLs da du die 'vier'' mit // überall suchst.
Mitglied: Kraemer
Kraemer 09.09.2016 um 12:05:33 Uhr
Goto Top
Zitat von @colinardo:
Kann man machen wenn man immer gleiche XMLs geliefert bekommt und die Verschachtelung gleich bleibt. Ich dachte jetzt eher an unterschiedlich verschachtelte XMLs da du die 'vier'' mit // überall suchst.
Ah, das hatte ich beim copy/paste nicht bedacht. Ist noch ein "Relikt" vom Anfang meines Skriptes. Da fehlt die nun saubere Struktur noch.