none
XML-Element über den Namen ansprechen RRS feed

  • Frage

  • Gutem Morgen,

    leider bin ich nicht so der XML-Held und weiß daher auch nicht so richtig, wie ich nach einer Lösung suchen soll.

    Meine XML-Datei hat den folgenden Aufbau:

    <?xml version="1.0" encoding="utf-8"?>
    <System>
    	<Umgebungen>
    		<Umgebung Name="Testanlage">
    			<Mandant Name ="Aussendienst">
    				<Server1>xyz1</Server1>
    				<Server2>xyz2</Server2>
    			</Mandant>
    			<Mandant Name="Innendienst">
    				<Server1>xyz3</Server1>
    				<Server2>xyz4</Server2>
    			</Mandant>
    		</Umgebung>
    		<Umgebung Name="Produktion">
    			<Mandant Name ="Aussendienst">
    				<Server1>abc1</Server1>
    				<Server2>abc2</Server2>
    			</Mandant>
    			<Mandant Name="Innendienst">
    				<Server1>abc3</Server1>
    				<Server2>abc4</Server2>
    			</Mandant>
    		</Umgebung>
    	</Umgebungen>
    </System>

    Diese XML-Datei importiere ich in PowerShell. Das funktioniert; alles gut soweit.

    Jetzt möchte ich gerne in meiner Variablen eine Umgebungen und einen Mandant ganz gezielt über den Namen ansprechen. Leider habe ich keine Idee, wie das funktioniert. Tagelanges Suchen und ausprobieren ist leider erfolglos geblieben und auch meine Kollegen wissen keinen Rat. Ich stelle mir das so vor, wie bei Excel, wenn ich ein Arbeitsblatt über den Namen anspreche: Worksheets("<Arbeitsblattname>"). Also so ungefähr:

    <Variable>.System.Umgebungen.Umgebung('Testanlage').Mandant('Innendienst').Server1

    Leider funktioniert es so nicht.
    Wie komme ich am Einfachsten an meinen Server1?

    Für jeden Lösungsvorschlag oder -ansatz bin ich dankbar.

    Vielen Dank und viele Grüße

    Sabine

    Montag, 29. Oktober 2018 07:59

Antworten

  • Hallo,

    so auf die schnell würde ich das wie folgt lösen:

    [xml]$xml = Get-Content C:\Temp\xml.xml
    ($xml.System.Umgebungen.Umgebung | Where-Object {$_.Name -eq "Produktion"}).Mandant.Server1

    gibt aber bestimmt eine eleganter Lösung.

    lg

    Montag, 29. Oktober 2018 12:59
  • Geht schon - mit XPath und Select-XML:

    $TestAussen = select-xml $xmldoc -XPath "//System//Umgebungen//Umgebung[@Name='Testanlage']//Mandant[@Name='Aussendienst']"

    Aber aufpassen, das ist komplett case sensitive. Es hat sich bewährt, Node- und Attributnamen komplett klein zu schreiben und nur Attributwerte bzw. Textinhalte bei Bedarf "richtig". Wenn Du alleine schon Attributinhalte case-insensitive suchen willst, mußt Du ne komplette Umwandlung in Klein- oder Großbuchstaben machen und erst dann suchen.

    Und es hat sich auch bewährt, für einfaches Skripting auf Elemente wie <Server1>abc1</Server1> zu verzichten und stattdessen <server1 name="abc1"/> zu verwenden :)

    Oder noch besser <server num="1" name="abc"/>. Node-Elemente möglichst generisch, differenzieren durch Attribute.


    Greetings/Grüße, Martin - https://mvp.microsoft.com/en-us/PublicProfile/5000017 Mal ein gutes Buch über GPOs lesen? - http://www.amazon.de/Windows-Server-2012--8-Gruppenrichtlinien/dp/3866456956 Good or bad GPOs? My blog - http://evilgpo.blogspot.com And if IT bothers me? Coke bottle design refreshment - http://sdrv.ms/14t35cq

    Montag, 29. Oktober 2018 15:44
  • Wie immer führen viele Wege zum Ziel.

    Noch mal zurück auf Anfang:
    XML bietet dir nur eine Schema-Definition ähnlich eine SQL-Tabelle, mit dem Unterschied, dass man eine XML-Knoten-Hierarchie bilden kann.

    Alle XML-Namen "<Name Attr1="" Attr2="">....</Name>" sind wie Spaltennamen zu sehen.
    Die Direktansprache funktioniert natürlich nur über die Knoten-/Attributnamen.

    Wenn man nach Inhalten sucht, kommst du halt um Suchfunktionen nicht herum.

    Alternativ kannst du jedoch ebenso statt "<Mandant>" auch einen Knoten "<Innendienst>" definieren, der dann direkt ansprechbar ist. Das macht die XML-Verarbeitung aber ggf. unflexibel.

    <?xml version="1.0" encoding="utf-8"?>
    <System>
    	<Umgebungen>
    		<Testanlage>
    			<Aussendienst>
    				<Server Name="xyz1" />
    				<Server Name="xyz2" />
    			</Aussendienst>
    			<Innendienst>
    				<Server Name="xyz3" />
    				<Server Name="xyz4" />
    			</Innendienst>
    		</Umgebung>
    		<Produktion>
    			<Aussendienst>
    				<Server Name="xyz5" />
    				<Server Name="xyz6" />
    			</Aussendienst>
    			<Innendienst>
    				<Server Name="xyz7" />
    				<Server Name="xyz8" />
    			</Innendienst>
    		</Produktion
    	</Umgebungen>
    </System>

    Somit hast du dann wieder die Ansprache

    $xml.Umgebungen.Testanlage.Aussendienst.Server

    Wobei Server dann eben als Array ansprechbar sind.
    $xml.Umgebungen.Testanlage.Aussendienst.Server[0].Attributes["Name"]

    $xml.Umgebungen.Testanlage.Aussendienst.Server.Count = Anzahl Knoten.
    Das Ganze geht natürlich auch variabel, da sichdie Knotennamen auchauslesen lassen:

    $xml.Umgebungen.Count = Anzahl Knoten
    $xml.Umgebungen[0].Name = Testanlage
    $xml.Umgebungen[1].Name = Produktion

    $Name = $xml.Umgebungen[0].Name
    $xml.Umgebungen[$Name].Innendienst

    usw. usf.

    Noch ein Unterschied:
    select-xml liest ein Dokument ein, durchsucht es, liefert das Ergebnis und verwirft das Dokument dann wieder.
    Jedes neue select-xml wiederholt den gesamten Vorgang.
    select-xml kann man dann verwenden, wenn man nur 1 Mal auf das Dokument zugreifen will.

    Liest man das Dokument aber direkt ein, kann man es mittels "$xml.SelectNodes" oder "$xml.SelectSingleNode" immer wieder durchsuchen.

    Montag, 29. Oktober 2018 17:20

Alle Antworten

  • Nachtrag:

    Ich bin nicht an diesen Aufbau der XMl-Datei gebunden. Wenn einen andere Struktur eine einfachere Lösung bietet, kann ich entsprechend umbauen.

    Grüße


    • Bearbeitet kleo-patra Montag, 29. Oktober 2018 08:06
    Montag, 29. Oktober 2018 08:01
  • Hier findest du die Beschreibung:
    https://www.windowspro.de/script/xml-powershell-elemente-attribute-auslesen-textknoten-anzeigen

    Statt Server1....ServerN würde ich auch hier einfach nur "Server" als Id verwenden, dann kannst du wieder per Arrayindex zugreifen.

    Montag, 29. Oktober 2018 08:30
  • Hallo bfuerchau,

    vielen Dank für Deine Antwort. Bei meiner Suche bin ich auch über diese Seite gestolpert. Leider habe ich dort nichts gefunden, was mir weiterhilft. (Oder ich habe es gefunden, aber leider nicht verstanden; das ist durchaus möglich.)

    Anmerkung zu Deinem Vorschlag, auf die Server als Array zuzugreifen:

    Ich habe hier nur einen exemplarischen Aufbau der Datei angegeben. Im echten Leben haben die Server richtige Namen (zum Beispiel DBServer1 und DBServer2 und SkriptServer1 und sowas.) Auf jeden Fall vielen Dank für die Anregung.

    
    • Bearbeitet kleo-patra Montag, 29. Oktober 2018 10:05 Tippfehler
    Montag, 29. Oktober 2018 10:05
  • Zuerst erstellst du eine Variable vom Typ XML:

    $xml = New-Object System.XML.XMLDocument

    Dann lädst du dein Dokument:

    $xml.Load("MyXml.xml")

    Der Zugriff auf die Knoten erfolgt nun über die Namen direkt:

    $xml.System.Umgebungen.Umgebung[0].Mandant[0].Server1

    Du kannst das ganze auch interaktiv durchführen.
    Wenn du nur die 1. Ebene ausgibst, werden nur diese ELemente angezeigt. Die angezeigten Namen kannstdu dann direkt verwenden.

    Hier ein weiteres Beispiel:
    https://www.codeproject.com/Articles/61900/PowerShell-and-XML

    Montag, 29. Oktober 2018 11:43
  • Hallo,

    so auf die schnell würde ich das wie folgt lösen:

    [xml]$xml = Get-Content C:\Temp\xml.xml
    ($xml.System.Umgebungen.Umgebung | Where-Object {$_.Name -eq "Produktion"}).Mandant.Server1

    gibt aber bestimmt eine eleganter Lösung.

    lg

    Montag, 29. Oktober 2018 12:59
  • Vielen Dank für Eure Antworten.

    Wenn ich das jetzt richtig verstehe, gibt es eine Lösung, so wie ich sie mir vorstelle, wohl einfach nicht.

    Mein Workaround entspricht ungefähr der Lösung von Schlieng:

    [xml]$i=gc -Path 'C:\tmp\Umgebungen.xml'
    $j=($i.System.Umgebungen.Umgebung|where{$_.name -eq 'Testanlage'}).Mandant|where{$_.Name -eq 'Innendienst'}

    Wenn jemand noch eine weitere Idee hat, würde ich mich freuen, wenn er sie hier postet.

    Vielen Dank und liebe Grüße

    Sabine

    Montag, 29. Oktober 2018 13:26
  • Geht schon - mit XPath und Select-XML:

    $TestAussen = select-xml $xmldoc -XPath "//System//Umgebungen//Umgebung[@Name='Testanlage']//Mandant[@Name='Aussendienst']"

    Aber aufpassen, das ist komplett case sensitive. Es hat sich bewährt, Node- und Attributnamen komplett klein zu schreiben und nur Attributwerte bzw. Textinhalte bei Bedarf "richtig". Wenn Du alleine schon Attributinhalte case-insensitive suchen willst, mußt Du ne komplette Umwandlung in Klein- oder Großbuchstaben machen und erst dann suchen.

    Und es hat sich auch bewährt, für einfaches Skripting auf Elemente wie <Server1>abc1</Server1> zu verzichten und stattdessen <server1 name="abc1"/> zu verwenden :)

    Oder noch besser <server num="1" name="abc"/>. Node-Elemente möglichst generisch, differenzieren durch Attribute.


    Greetings/Grüße, Martin - https://mvp.microsoft.com/en-us/PublicProfile/5000017 Mal ein gutes Buch über GPOs lesen? - http://www.amazon.de/Windows-Server-2012--8-Gruppenrichtlinien/dp/3866456956 Good or bad GPOs? My blog - http://evilgpo.blogspot.com And if IT bothers me? Coke bottle design refreshment - http://sdrv.ms/14t35cq

    Montag, 29. Oktober 2018 15:44
  • Wie immer führen viele Wege zum Ziel.

    Noch mal zurück auf Anfang:
    XML bietet dir nur eine Schema-Definition ähnlich eine SQL-Tabelle, mit dem Unterschied, dass man eine XML-Knoten-Hierarchie bilden kann.

    Alle XML-Namen "<Name Attr1="" Attr2="">....</Name>" sind wie Spaltennamen zu sehen.
    Die Direktansprache funktioniert natürlich nur über die Knoten-/Attributnamen.

    Wenn man nach Inhalten sucht, kommst du halt um Suchfunktionen nicht herum.

    Alternativ kannst du jedoch ebenso statt "<Mandant>" auch einen Knoten "<Innendienst>" definieren, der dann direkt ansprechbar ist. Das macht die XML-Verarbeitung aber ggf. unflexibel.

    <?xml version="1.0" encoding="utf-8"?>
    <System>
    	<Umgebungen>
    		<Testanlage>
    			<Aussendienst>
    				<Server Name="xyz1" />
    				<Server Name="xyz2" />
    			</Aussendienst>
    			<Innendienst>
    				<Server Name="xyz3" />
    				<Server Name="xyz4" />
    			</Innendienst>
    		</Umgebung>
    		<Produktion>
    			<Aussendienst>
    				<Server Name="xyz5" />
    				<Server Name="xyz6" />
    			</Aussendienst>
    			<Innendienst>
    				<Server Name="xyz7" />
    				<Server Name="xyz8" />
    			</Innendienst>
    		</Produktion
    	</Umgebungen>
    </System>

    Somit hast du dann wieder die Ansprache

    $xml.Umgebungen.Testanlage.Aussendienst.Server

    Wobei Server dann eben als Array ansprechbar sind.
    $xml.Umgebungen.Testanlage.Aussendienst.Server[0].Attributes["Name"]

    $xml.Umgebungen.Testanlage.Aussendienst.Server.Count = Anzahl Knoten.
    Das Ganze geht natürlich auch variabel, da sichdie Knotennamen auchauslesen lassen:

    $xml.Umgebungen.Count = Anzahl Knoten
    $xml.Umgebungen[0].Name = Testanlage
    $xml.Umgebungen[1].Name = Produktion

    $Name = $xml.Umgebungen[0].Name
    $xml.Umgebungen[$Name].Innendienst

    usw. usf.

    Noch ein Unterschied:
    select-xml liest ein Dokument ein, durchsucht es, liefert das Ergebnis und verwirft das Dokument dann wieder.
    Jedes neue select-xml wiederholt den gesamten Vorgang.
    select-xml kann man dann verwenden, wenn man nur 1 Mal auf das Dokument zugreifen will.

    Liest man das Dokument aber direkt ein, kann man es mittels "$xml.SelectNodes" oder "$xml.SelectSingleNode" immer wieder durchsuchen.

    Montag, 29. Oktober 2018 17:20