Powershell: XML auslesen

ral9004

Lieutenant
Registriert
Dez. 2017
Beiträge
523
Guten Abend

Die XML Datei besteht aus solchen Items

<?xml version="1.0" encoding="UTF-8"?>
<Servers>
<Putty>
<Node Type="0">
<DisplayName>KSM</DisplayName>
<Node Type="0">
<DisplayName>Core</DisplayName>
<Node Type="1">
<SavedSession>Default Settings</SavedSession>
<DisplayName>Donald Duck</DisplayName>
<UID>{247F341D-CE83-483C-90D9-97131D799DC6}</UID>
<ServerName>127.0.0.1</ServerName>
<PuttyConType>4</PuttyConType>
<Port>0</Port>
<UserName></UserName>
<Password>3p+F4C0DGcI=</Password>
<PasswordDelay>0</PasswordDelay>
<CLParams>10.56.145.227 -ssh</CLParams>
<ScriptDelay>0</ScriptDelay>
</Node>

Ein erster Test, den Inhalt einer Zeile auszugeben schlug fehl. D.h. "write-host" schreibt kein Byte auf den Bildschirm.

Variante 1
$fl_xml_mo = New-Object System.XML.XMLDocument
$fl_xml_mo.Load(".\hosts-montag.xml")
write-host $ovf.Servers.Putty.DisplayName.Servername.OuterXml

Variante 2
$fl_xml_mo = New-Object System.XML.XMLDocument
$fl_xml_mo.Load(".\hosts-montag.xml")
write-host $ovf.Servers.Putty.DisplayName.Servername.item

Wo liegt mein(e) Fehler?

Viele Grüsse
 
  • Gefällt mir
Reaktionen: ral9004
@tollertyp
Ich habe die Variablen für dieses Beispiel ersetzt. D.h. es wird immer nur entweder oder verwendet

@Korben2206
Das XML wird von einer Applikation erstellt. Das kann ich nicht steuern

@derlorenz Danke für das Tut. Das werde ich mir in ein paar Stunden anschauen.
 
ral9004 schrieb:
@Korben2206
Das XML wird von einer Applikation erstellt. Das kann ich nicht steuern
Ab zum produkt manager / verantwortlichen und sagen, bitte gib mir valide xml ;)
/E das zu fixen wäre auch möglich mit anderem code - so das du den output bekommst denn du möchtest, ist einfach 100x schwerer aufwendiger, als ein valides xml zu verwenden.
 
  • Gefällt mir
Reaktionen: tollertyp
@Kenny [CH]
Eine XML Datei auch nur ein Textfile. Ich benötige zwei Zeilen pro Element.
Wenn die XML Validerung unzureichend ist, dann behandle ich es als Text.
D.h. die zwei Zeilen Suchen und in eine Datei schreiben.

Ich nutze das, was gegeben ist. Bzw. was zum Ziel führt.
 
Du kannst gerne das file 'zuschneiden' (cat/tail/head/sed etc - linux user kenne diese tools besser daher diese namen), du kanmst auch mit regex dinge erkennen und entfernen etc pp. Nur wird es schwer und aufwändiger. Parsen von files 'sucks' wenn du denn parser selber schreiben must.
Der code der so entsteht ^ kann sehr anfällig sein und womöglich oft/öfters brechen. Wenn das xml valide wäre, wäre der task viel einfacher und wenn richtig gemach stabil.

Ich bin nur nicht fan von schlechter sw output mit 'komischen handständen' zu verarbeiten, wenn die erst sw nur ein valides, standardisiertes format ausgeben würde.
Für was hat man z.b. A4/A5 etc wenn dann jeder wieder sein eigenes papier format bestimmt/verwendet?
Wieso wird das überhaupt als xml ausgegeben, wenn es nicht mal valide geschrieben wird (denn standard einhält)? Hätte man auch einfach ein txt schreiben können - oder was entwickler lieben json...
 
Wenn falsches XML okay ist, dann lies die Struktur halt anders aus.

Aber es kann ja auch nichts ausgeben, ich helfe mal:

Welchen Wert würdest du denn hier erwarten?
Servers.Putty.DisplayName.Servername

Nochmal das XML zur Hilfestellung:
Code:
<?xml version="1.0" encoding="UTF-8"?>
<Servers>   <--- Servers
   <Putty>    <--- Putty
       <Node Type="0">   <-- Node fehlt bei dir ganz offensichtlich
           <DisplayName>KSM</DisplayName> <-- Displayname, aber hier gibt es keinen Servername?
           <Node Type="0">  <-- fehlt auch...
               <DisplayName>Core</DisplayName><-- ist der DisplayName korrekt?
               <Node Type="1"> <--- Heureka, den Node meinen wir
                   <SavedSession>Default Settings</SavedSession>
                   <DisplayName>Donald Duck</DisplayName> <-- ist der DisplayName korrekt?
                   <UID>{247F341D-CE83-483C-90D9-97131D799DC6}</UID>
                   <ServerName>127.0.0.1</ServerName>   <-- Ach, hier wäre der Servername?
                   <PuttyConType>4</PuttyConType>
                   <Port>0</Port>
                   <UserName></UserName>
                   <Password>3p+F4C0DGcI=</Password>
                   <PasswordDelay>0</PasswordDelay>
                   <CLParams>10.56.145.227 -ssh</CLParams>
                   <ScriptDelay>0</ScriptDelay>
               </Node>

Vielleicht kann das Ding die XML sogar lesen, aber deine Abfrage ist halt offensichtlich falsch.
Vermutlich solltest du im DOM nach passenden Knoten suchen, die zufällig hier auch noch Node heißen. Node mit Attribut type="1" wären das wohl...

Evtl hilft das hier:
https://ilovepowershell.com/2015/09/11/searching-xml-nodes-by-attribute-name-with-select-xml/

@Kenny [CH]: Oder mit noch weniger syntaktischem Overhead auch yml...
 
Zuletzt bearbeitet:
Für ein bißchen Abkürzung:
PowerShell:
[xml] $xmlDocument = Get-Content path/to/data.xml
reicht völlig, um ein xmldocument Object zu erstellen. PS ist bei sowas sehr freizügig.

Dann mal in Richtung XPath schauen (gut dokumentiert) und dann $xmlDoc.SelectSingleNode() und .SelectNodes() benutzen.

ZB so:
PowerShell:
$NodesOfType1 = $xmlDoc.SelectNodes('/Servers/Putty/Node[Type="1"]')

Dann ist $NodesOfType1 eine Liste der Knoten mit Attribute Type welches den Wert 1 hat; jedes Element da drin hat dann die Eigenschaften, die im XML unter diesem Knoten zugeordnet waren. Also in diesem Fall .DisplayName und .ServerName.

Das wirft dann auch rechtzeitig Exceptions, wenn die XML-Struktur tatsächlich nicht well-formed sein sollte.

Und bedenken: XML ist case sensitive im Gegensatz zu den meisten Dingen in PS.
 
  • Gefällt mir
Reaktionen: tollertyp, ral9004 und Korben2206
Hallo Iqra

Eine Verständnisfrage. Das Objekt $NodesOfType1 enthält dann alle Elemente vom vom Typ "Node[Type="1"]'"

$NodesOfType1 = $ovf.SelectNodes('/Servers/Putty/Node[Type="1"]')

D.h. ich kann den Inhalt z.B. mit einer For Each Schlaufe auflisten. Oder geht das einfacher?

Das Objektmodell habe ich anscheinend noch nicht verstanden. Auf dem Bildschirm wird nichts geschrieben. Und die Klein / Grossschreibweise habe ich beachtet.

foreach ($node in $NodesOfType1) {
$dname =$node.attributes['DisplayName'].value
write-host $dname
}

Viele Grüsse
 
Also mit Xml kann ich nicht wirklich viel helfen, aber folgendes hat (mit einem validen XML) funktioniert:
PowerShell:
[xml]$xmlDocument = Get-Content D:\Test\test.xml
$result = $xmlDocument.Servers.Putty.GetEnumerator() | Where Type -eq "1"
$result | % { Write-Host $_.DisplayName }

PS: wenn du beim Einlesen der XML keine Fehlermeldung bekommst, dann ist die von dir oben gepostete XML nicht die, welche du verwendest... das hätte die Fehlersuche vereinfachen können...
Inhalte kann/sollte man natürlich anpassen, aber nicht das Beispiel unbrauchbar machen und so Leute auf die falsche Spur schicken...
 
Korben2206 schrieb:
PS: wenn du beim Einlesen der XML keine Fehlermeldung bekommst, dann ist die von dir oben gepostete XML nicht die, welche du verwendest... das hätte die Fehlersuche vereinfachen können...
Inhalte kann/sollte man natürlich anpassen, aber nicht das Beispiel unbrauchbar machen und so Leute auf die falsche Spur schicken...
Guten Abend Korben2206
Diese Aussage stimme ich 100% zu.
Beim austauschen einer Variable habe ich ein Exemplar übersehen.
Die ganze XML Datei enthält über 500 solcher von mir geposteten Elemente.

Aber jetzt haben wir doch ein Rätsel. Die XML Datei ist noch valide und dennoch habe ich keine Fehlermeldung erhalten. Zum Glück muss ich diesen Sachverhalt nicht lösen. Nur zwei Zeilen aus jedem Node heraus lesen.

Viele Grüsse
 
Zurück
Oben