Powershell: XML Elemente durchgehen

M

MrWeedster

Gast
Hallo.

Nach stundenlangem suchen und rumprobieren komm ich auf keinen gruenen Zweig.

Ich moechte folgende XML einlesen und verarbeiten:

Code:
<?xml version="1.0" encoding="UTF-8"?>
<Root>
  <Location name ="Buxthehude">
    <TOPIC>
      <from>one_server</from>
      <to>another_server</to>
      <replication_dir name="dir1">
        <exception_dir>dir1\exc_dir1</exception_dir>
        <exception_dir>dir1\exc_dir2</exception_dir>
      </replication_dir>
      <replication_dir name="dir2">
        <exception_dir>dir2\exc_dir3</exception_dir>
        <exception_dir>dir2\exc_dir4</exception_dir>
        <exception_dir>dir2\exc_dir5</exception_dir>
        <exception_dir>dir2\exc_dir6</exception_dir>
        <exception_dir>dir2\exc_dir7</exception_dir>
      </replication_dir>
    </TOPIC>
  </Location>
</Root>

Der Code sieht so aus:

Code:
[xml] $doc = [xml](Get-Content -Path $xmlfile)
$topic_node = $doc.SelectSingleNode("/Root/Location[@name='Buxthehude']/TOPIC")

$source_server = $topic_node.from
$target_server = $topic_node.to

foreach ($replica_dir in $topic_node.replication_dir) {
	write-host "would replicate $replica_dir"
}

Die write-host Ausgabe bringt aber nix.
Wie muss ich richtig auf die Elemente zugreifen das ich sie verarbeiten kann?
Ich will alle replication_dirs in der Schleife durchgehen und verarbeiten.

Wenn ich das ganze in der Shell direkt durchspiele, kann ich mir mit
$topic_node.replication_dir
den Inhalt der replication_dirs anschauen:


Code:
PS D:\work\scripts> $topic_node.replication_dir

name                                                        exception_dir
----                                                        -------------
dir_1                                                        {dir_1\exc_dir1, dir_1\exc_dir2}
dir_2                                                        {dir_2\exc_dir3, dir_2\exc_dir4}

Aber warum funktioniert das nicht in dem foreach oben? Wo ist mein Denkfehler?
Ich wuerde erwarten das ich mit foreach ($replica_dir in $topic_node.replication_dir)
alle Elemente der replication_dirs erhalte. Sprich: dir_1, dir_2

Ich hab da alles moegliche schon hingeschrieben (also bei ... in $topic_node.replication.dir, z.B. .name und .Elements und was weiss ich, aber iwie haut nix hin)

WTF is wrong with me?


Um meinem gleich darauf folgenden Problem zuvorzukommen, gleich hier noch die
anschliessende Frage: exception_dirs sind ja untergeordnete, geschachtelte
Elemente. Wie kann ich die anschliessend alle durchgehen?
(Vermutlich beantwortet sich die Frage mit der Antwort auf die erste)


Ich bin sicher das es eine simple Loesung ist :>
Ergänzung ()

K, der Kacknoob hats doch noch selbst hingekriegt:

Fehler war in dem Foreach:

Statt:
Code:
foreach ($replication_dir in $topic_node.replication_dir) {

Code:
foreach ($replication_dir in $topic_node.replication_dir | %{$_.name}) {

Und schon kriegt man die Elemente.

(Warum man nur erst immer Topics erstellen muss um kurz danach selbst auf die Loesung zu kommen ^^)

o_O
Ergänzung ()

Jo, trotz langem Probieren komm ich jetzt doch nicht mehr weiter:

Ich moechte jetzt eine Ebene tiefer gehen und die exception_dirs auslesen und jedes durchgehen.

Wie greif ich auf die zu?

Ich wuerde jetzt in der foreach als naechstes den Knoten aendern:

Code:
[xml] $doc = [xml](Get-Content -Path $xmlfile)
$topic_node = $doc.SelectSingleNode("/Root/Location[@name='Buxthehude']/TOPIC")
     
$source_server = $topic_node.from
$target_server = $topic_node.to
     
foreach ($replica_dir in $topic_node.replication_dir) {
    write-host "would replicate $replica_dir"
    $exception_node = $topic_node.replication_dir[@name='$replica_dir']

    Pseudo-Code:
    foreach ($exception_dir in $exception_node) {
       function aufrufen
    }
}

So rein logisch waere das doch der Weg, oder?

Natuerlich funktioniert das nicht:

Code:
Der Splat-Operator "@" kann nicht zum Verweisen auf Variablen in einem Ausdruck verwendet werden. "@name" kann nur als
Argument für einen Befehl verwendet werden. Verwenden Sie "$name" zum Verweisen auf Variablen in einem Ausdruck.

Bla, wtf? Oben gings doch genauso.

Wie muss ich auf die exception_dirs zugreifen?
 
Zuletzt bearbeitet von einem Moderator:
Warum machst du es dir so schwer?
Code:
$doc = [xml](Get-Content "data.xml")
$doc.Root.Location.TOPIC.replication_dir | % {
    echo "Name: $($_.name)"
    $_.exception_dir | % {
        echo "`t$_"
    }
}
Code:
Name: dir1
        dir1\exc_dir1
        dir1\exc_dir2
Name: dir2
        dir2\exc_dir3
        dir2\exc_dir4
        dir2\exc_dir5
        dir2\exc_dir6
        dir2\exc_dir7
Du bist in deinem Code doch übrigens schon im repication_dir-Node. Warum dann nochmal von vorn anfangen und heraussuchen? Übrigens sollte $replica_dir ein XmlElement sein. Du müsstest ergo auf das Attribut $replica_dir.name prüfen. Weiterhin funktioniert es nicht, da du oben das @ im XPath drin hast, was ein String ist. Unten verwendest du es aber nicht in einem String und der @-Operator hat in PowerShell eine andere Bedeutung.
 
In PS kannst du einfach über sämtliche Attribute Iterieren:

Code:
PS C:\> $xml.Root.Location.TOPIC.replication_dir.exception_dir
dir1\exc_dir1
dir1\exc_dir2
dir2\exc_dir3
dir2\exc_dir4
dir2\exc_dir5
dir2\exc_dir6
dir2\exc_dir7

natürlich kannst du auch direkt auf die einzelnen Nodes zugreifen etc. Hilfe gibt dabei die Get-Member Methode:

Code:
PS C:\> $xml | Get-Member


   TypeName: System.Xml.XmlDocument

Name                        MemberType            Definition
----                        ----------            ----------
ToString                    CodeMethod            static string XmlNode(psobject instance)
AppendChild                 Method                System.Xml.XmlNode AppendChild(System.Xml.XmlNode newChild)
Clone                       Method                System.Xml.XmlNode Clone(), System.Object ICloneable.Clone()
CloneNode                   Method                System.Xml.XmlNode CloneNode(bool deep)
CreateAttribute             Method                System.Xml.XmlAttribute CreateAttribute(string name), System.Xml.XmlAttribute CreateAttribute(string qualifiedName, string nam...
CreateCDataSection          Method                System.Xml.XmlCDataSection CreateCDataSection(string data)
CreateComment               Method                System.Xml.XmlComment CreateComment(string data)
CreateDocumentFragment      Method                System.Xml.XmlDocumentFragment CreateDocumentFragment()
CreateDocumentType          Method                System.Xml.XmlDocumentType CreateDocumentType(string name, string publicId, string systemId, string internalSubset)
CreateElement               Method                System.Xml.XmlElement CreateElement(string name), System.Xml.XmlElement CreateElement(string qualifiedName, string namespaceUR...
CreateEntityReference       Method                System.Xml.XmlEntityReference CreateEntityReference(string name)
CreateNavigator             Method                System.Xml.XPath.XPathNavigator CreateNavigator(), System.Xml.XPath.XPathNavigator IXPathNavigable.CreateNavigator()
CreateNode                  Method                System.Xml.XmlNode CreateNode(System.Xml.XmlNodeType type, string prefix, string name, string namespaceURI), System.Xml.XmlNod...
CreateProcessingInstruction Method                System.Xml.XmlProcessingInstruction CreateProcessingInstruction(string target, string data)
CreateSignificantWhitespace Method                System.Xml.XmlSignificantWhitespace CreateSignificantWhitespace(string text)
CreateTextNode              Method                System.Xml.XmlText CreateTextNode(string text)
CreateWhitespace            Method                System.Xml.XmlWhitespace CreateWhitespace(string text)
CreateXmlDeclaration        Method                System.Xml.XmlDeclaration CreateXmlDeclaration(string version, string encoding, string standalone)
Equals                      Method                bool Equals(System.Object obj)
GetElementById              Method                System.Xml.XmlElement GetElementById(string elementId)
GetElementsByTagName        Method                System.Xml.XmlNodeList GetElementsByTagName(string name), System.Xml.XmlNodeList GetElementsByTagName(string localName, string...
GetEnumerator               Method                System.Collections.IEnumerator GetEnumerator(), System.Collections.IEnumerator IEnumerable.GetEnumerator()
GetHashCode                 Method                int GetHashCode()
GetNamespaceOfPrefix        Method                string GetNamespaceOfPrefix(string prefix)
GetPrefixOfNamespace        Method                string GetPrefixOfNamespace(string namespaceURI)
GetType                     Method                type GetType()
ImportNode                  Method                System.Xml.XmlNode ImportNode(System.Xml.XmlNode node, bool deep)
InsertAfter                 Method                System.Xml.XmlNode InsertAfter(System.Xml.XmlNode newChild, System.Xml.XmlNode refChild)
InsertBefore                Method                System.Xml.XmlNode InsertBefore(System.Xml.XmlNode newChild, System.Xml.XmlNode refChild)
Load                        Method                void Load(string filename), void Load(System.IO.Stream inStream), void Load(System.IO.TextReader txtReader), void Load(System....
LoadXml                     Method                void LoadXml(string xml)
Normalize                   Method                void Normalize()
PrependChild                Method                System.Xml.XmlNode PrependChild(System.Xml.XmlNode newChild)
ReadNode                    Method                System.Xml.XmlNode ReadNode(System.Xml.XmlReader reader)
RemoveAll                   Method                void RemoveAll()
RemoveChild                 Method                System.Xml.XmlNode RemoveChild(System.Xml.XmlNode oldChild)
ReplaceChild                Method                System.Xml.XmlNode ReplaceChild(System.Xml.XmlNode newChild, System.Xml.XmlNode oldChild)
Save                        Method                void Save(string filename), void Save(System.IO.Stream outStream), void Save(System.IO.TextWriter writer), void Save(System.Xm...
SelectNodes                 Method                System.Xml.XmlNodeList SelectNodes(string xpath), System.Xml.XmlNodeList SelectNodes(string xpath, System.Xml.XmlNamespaceMana...
SelectSingleNode            Method                System.Xml.XmlNode SelectSingleNode(string xpath), System.Xml.XmlNode SelectSingleNode(string xpath, System.Xml.XmlNamespaceMa...
Supports                    Method                bool Supports(string feature, string version)
Validate                    Method                void Validate(System.Xml.Schema.ValidationEventHandler validationEventHandler), void Validate(System.Xml.Schema.ValidationEven...
WriteContentTo              Method                void WriteContentTo(System.Xml.XmlWriter xw)
WriteTo                     Method                void WriteTo(System.Xml.XmlWriter w)
Item                        ParameterizedProperty System.Xml.XmlElement Item(string name) {get;}, System.Xml.XmlElement Item(string localname, string ns) {get;}
Root                        Property              System.Xml.XmlElement Root {get;}
xml                         Property              string xml {get;set;}
 
Warum? Weil ich mein Perlwissen versuche auf Powershell zu adaptieren und dabei viel zu kompliziert denke, und die Besonderheiten der PS noch nicht so kenne. Ausserdem muss es wartbar sein von Leuten die keine Ahnung haben :>

Zu deinem Code: Hardcore ^^
Aber: Kann das sein das sich das auf PS >3.0, oder 4.0 bezieht? Ich bekomm da naemlich nichts raus, ausser:


Code:
PS d:\work\scripte> $doc = [xml](Get-Content "data.xml")
PS d:\work\scripte> $doc.Root.Location.TOPIC.replication_dir | % {
>> echo "Name: $($_.name)"
>> $_.exception_dir | % {
>> echo "$_"
>> }
>> }
>>
Name:

Unser Biz hat leider nur PS 2.0


Achja: Wenn ich per "Tab" versuche ab TOPIC auf Elemente zuzugreifen kommt kein Vorschlag mehr. Heisst das er findet nicht was ich will?


@expironec: Dein Code tut bei mir garnix.
Ich vermute ich muss in meinem Fall $doc.Root.Location.TOPIC.replication_dir.exception_dir nehmen, oder?


[E]
Anscheinend ist meine XML Datei nicht OK.
Mit ner Pseudodatei funktionierts.
Mal schaun...
 
Zuletzt bearbeitet von einem Moderator:
Ja, hab die xml file in die Variable $xml eingelesen.

OK, so kann das nicht funktionieren, da du im Array replication_dir String Objekte drin hast:

Code:
PS C:\> $xml.Root.Location.TOPIC.replication_dir.exception_dir[0]
dir1\exc_dir1
PS C:\> $xml.Root.Location.TOPIC.replication_dir.exception_dir[0].gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     String                                   System.Object

Also kannst du entweder über alle beinhaltenden Child's iterieren:

Code:
PS C:\> foreach($x in $xml.Root.Location.TOPIC.replication_dir.exception_dir){
>> Write-Host("Hello " + $x)
>> }
>>
Hello dir1\exc_dir1
Hello dir1\exc_dir2
Hello dir2\exc_dir3
Hello dir2\exc_dir4
Hello dir2\exc_dir5
Hello dir2\exc_dir6
Hello dir2\exc_dir7

Oder du grenzt es wie folgt ein:

Code:
PS C:\> ($xml.Root.Location.TOPIC.replication_dir | ?{$_.name -eq "dir1"}).exception_dir
dir1\exc_dir1
dir1\exc_dir2
 
Also, die XML war tatsaechlich nicht OK:
Hatte am Ende noch eine Platzhalterlocation drin ohne Inhalt:


Code:
<Location name="Honkenhausen">



</Location>
<Location />
</Replication>

Wenn das unten noch drinsteht findet er TOPIC nicht mehr.
Wenn ichs rausnehme gehts.


Ich denke jetzt bekomm ichs hin, Danke fuer eure Hilfe!
 

Ähnliche Themen

Zurück
Oben