Powershell XML.SelectNodes findet nichts
Hi,
Ich bekomme aus einem Programm eine XML die ich gerne erweitern möchte. Undzwar möchte ich '<Symbol>' kopieren und dabei die <Component> OPN_SOLL mit V15_SOLL ersetzen.
Wenn ich jetzt jedoch '$sourceNode = $XMLContend.SelectNodes("//Parts")' ausführe ist $sourceNode.Count "0".
Wie kann ich auf 'Parts' Zugreifen und das Objekt Component kopieren/adressieren
Powershell:
XML:
Danke.
Ich bekomme aus einem Programm eine XML die ich gerne erweitern möchte. Undzwar möchte ich '<Symbol>' kopieren und dabei die <Component> OPN_SOLL mit V15_SOLL ersetzen.
Wenn ich jetzt jedoch '$sourceNode = $XMLContend.SelectNodes("//Parts")' ausführe ist $sourceNode.Count "0".
Wie kann ich auf 'Parts' Zugreifen und das Objekt Component kopieren/adressieren
Powershell:
Set-Location -path C:\XMLtest
$XMLContend = [XML](Get-Content ".\OPN_ORG.xml")
$sourceNode = $XMLContend.SelectNodes("//Parts")
$sourceNode.Count
XML:
Danke.
Bitte markiere auch die Kommentare, die zur Lösung des Beitrags beigetragen haben
Content-ID: 1302715031
Url: https://administrator.de/contentid/1302715031
Ausgedruckt am: 19.12.2024 um 10:12 Uhr
14 Kommentare
Neuester Kommentar
XMLContend
Dazu sag ich jetzt mal nichts .1. Von deiner XML sieht man leider nur die Hälfte und dann auch noch als Bild?? Warum kein richtiger Text?. Bei SelectNodes ist es aber essentiell wichtig wenn Namespaces vorhanden sind, und die kann man hier leider nicht erkennen wenn man nur die Hälfte da von sieht. Bei Namespaces muss man einen Namespace-Manger erstellen und diesem den Namespace in dem sich der Knoten befindet deklarieren. Alternativ nimmt man sich statt SelectNodes zu verwenden den Baum der Powershell Objekte her und itteriert darüber bsp.
$xmlContend."SW.Blocks.FC".Parts | %{
$_
}
Danke.
Bitte.
Servus @SPSman,
@149569 hat schon einen wichtigen Hinweis gegeben. Wenn das XML-File Namespaces enthält muss man auch via Namespace-Prefix in der SelectNodes Methode arbeiten und einen Namespacemanager angeben in dem die Namespaces und dessen Prefixe enthalten sind.
Und wenn man genau auf das Bild schaut sieht man auch das es Namespaces enthält @149569, zwar nicht den Namespace an sich aber das xmlns reicht dann schon das zu behaupten .
Hier mal ein Beispiel-XML File das eine dem Parts-Knoten übergeordnete Namespace Deklaration in dem Knoten
enthält:
Darauf aubauend nun ein Skript das die Namespaces via Namespace-Manager bekannt macht und dann diesen in der SelectNodes Methode nutzt.:
Bei der folgenden Methode extrahiere ich alle enthaltenen Namespaces automatisch, du kannst diese aber auch manuell dem NamespaceManger hinzufügen via
Das Ergebnis des Codes macht aus dem Abschnitt:
dann diesen (du wolltest ja kopieren und dabei anpassen und nicht nur ersetzen, so jedenfalls habe ich deinen Text interpretiert)
Willst du stattdessen den Knoten nur ändern und nicht kopieren dann reicht es den Code der Schleife oben so abzuändern
Ergebnis ist dann...
Hier auch noch etwas Lektüre zum nachlesen
https://www.windowspro.de/script/xml-powershell-xpath-abfragen-namespace ...
Grüße Uwe
@149569 hat schon einen wichtigen Hinweis gegeben. Wenn das XML-File Namespaces enthält muss man auch via Namespace-Prefix in der SelectNodes Methode arbeiten und einen Namespacemanager angeben in dem die Namespaces und dessen Prefixe enthalten sind.
Und wenn man genau auf das Bild schaut sieht man auch das es Namespaces enthält @149569, zwar nicht den Namespace an sich aber das xmlns reicht dann schon das zu behaupten .
Hier mal ein Beispiel-XML File das eine dem Parts-Knoten übergeordnete Namespace Deklaration in dem Knoten
<FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v1">
<?xml version="1.0" encoding="utf-8"?>
<Document>
<SW.Blocks.CompileUnit ID="3" CompositionName="CompileUnits">
<AttributeList>
<NetworkSource>
<FlgNet xmlns="http://www.siemens.com/automation/Openness/SW/NetworkSource/FlgNet/v1">
<Parts>
<Access Scope="LocalVariable" UId="21">
<Symbol>
<Component Name="bInCondition" />
<Component Name="OPN_SOLL" />
</Symbol>
</Access>
<Access Scope="LocalVariable" UId="22">
<Symbol>
<Component Name="bOutDoSomething" />
</Symbol>
</Access>
<Part Name="Contact" UId="23" />
<Part Name="Coil" UId="24" />
</Parts>
<Wires>
<Wire UId="25">
<Powerrail />
<NameCon UId="23" Name="in" />
</Wire>
<Wire UId="26">
<IdentCon UId="21" />
<NameCon UId="23" Name="operand" />
</Wire>
<Wire UId="27">
<NameCon UId="23" Name="out" />
<NameCon UId="24" Name="in" />
</Wire>
<Wire UId="28">
<IdentCon UId="22" />
<NameCon UId="24" Name="operand" />
</Wire>
</Wires>
</FlgNet>
</NetworkSource>
<ProgrammingLanguage>LAD</ProgrammingLanguage>
</AttributeList>
<ObjectList>
<MultilingualText ID="4" CompositionName="Comment">
<ObjectList>
<MultilingualTextItem ID="5" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text>This network uses an input condition to activate an output action</Text>
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
<MultilingualText ID="6" CompositionName="Title">
<ObjectList>
<MultilingualTextItem ID="7" CompositionName="Items">
<AttributeList>
<Culture>en-US</Culture>
<Text>Simple contact/coil network</Text>
</AttributeList>
</MultilingualTextItem>
</ObjectList>
</MultilingualText>
</ObjectList>
</SW.Blocks.CompileUnit>
</Document>
Darauf aubauend nun ein Skript das die Namespaces via Namespace-Manager bekannt macht und dann diesen in der SelectNodes Methode nutzt.:
Bei der folgenden Methode extrahiere ich alle enthaltenen Namespaces automatisch, du kannst diese aber auch manuell dem NamespaceManger hinzufügen via
$ns.AddNamespace("prefix","namespace-uri")
# xml Objekt erstellen
$xml = New-Object XML
# XML Dokument laden
$xml.Load('D:\spsman\demo.xml')
# NamespaceManager erstellen
[System.Xml.XmlNamespaceManager]$ns = new-Object System.Xml.XmlNamespaceManager $xml.NameTable
# Extrahiere alle Namespaces und fürge diese dem Namespacemanager-Objekt hinzu
$xml.SelectNodes('//*') | ?{$_.NamespaceURI -ne ''} | select @{n='Prefix';e={$_.GetPrefixOfNamespace($_.NamespaceURI)}},NamespaceURI | select Prefix,NamespaceURI -unique | %{if($_.Prefix -eq ''){$_.Prefix = 'default'};$ns.AddNamespace($_.Prefix,$_.NamespaceURI)}
# Abfrage
$nodes = $xml.SelectNodes("//default:Parts//default:Component[@Name='OPN_SOLL']",$ns)
$nodes | %{
# Klone Knoten
$new = $_.Clone()
# setze Name Attribut auf neuen Wert
$new.SetAttribute("Name","V15_SOLL")
# hänge neuen Knoten am parent Knoten an
[void]$_.parentNode.appendChild($new)
}
# speichere
$xml.Save('D:\spsman\demo_out.xml')
Das Ergebnis des Codes macht aus dem Abschnitt:
<Symbol>
<Component Name="bInCondition" />
<Component Name="OPN_SOLL" />
</Symbol>
<Symbol>
<Component Name="bInCondition" />
<Component Name="OPN_SOLL" />
<Component Name="V15_SOLL" />
</Symbol>
# ...
$nodes | %{
$_.SetAttribute("Name","V15_SOLL")
}
# ....
<Symbol>
<Component Name="bInCondition" />
<Component Name="V15_SOLL" />
</Symbol>
Hier auch noch etwas Lektüre zum nachlesen
https://www.windowspro.de/script/xml-powershell-xpath-abfragen-namespace ...
Grüße Uwe
Zitat von @SPSman:
mit dem Thema Namespaces habe ich mich noch nicht beschäftigt. Werde ich wohl noch müssen. Das "Parts" ist jedoch nicht "hineingefirmelt" sondern genau so aus dem Siemens Programm gekommen ;)
VG Rob
mit dem Thema Namespaces habe ich mich noch nicht beschäftigt. Werde ich wohl noch müssen. Das "Parts" ist jedoch nicht "hineingefirmelt" sondern genau so aus dem Siemens Programm gekommen ;)
VG Rob
Na dann hat @colinardo es dir ja schon auf den Silbertablett serviert.
Zitat von @SPSman:
Und was für ein Problem er mit der Fehlermeldung meint ist mit auch noch Schleierhaft.
1. Du setzt den Knotennamen in der XPath Query in Hochkommas, das ist falsch, das brauchst du hier verwechselst hier Powershell mit XPath Syntax.Und was für ein Problem er mit der Fehlermeldung meint ist mit auch noch Schleierhaft.
2. Bei dem Knoten "SW.Blocks.CompileUnit" brauchst du keinen Namespacemanager, da dieser Knoten nicht unterhalb der Baumstruktur liegt welcher den Namespace definiert! Für die XPath Abfrage brauchen wir also den Namespacemanger nur wenn wir Knoten selektieren wollen die in einer Namespace definition, das ist bei deiner Aufgabenstellung nur für den Component Knoten mit dem Attribute Name="OPN_SOLL" der Fall.
Er hat trotz "SelectsingleNote" 2 objekte. Warum?
3. Das du noch 2 Objekte in der Variablen hast liegt daran das du diese wohl in der ISE vorher mal gefüllt hast und diese immer noch alten Inhalt ausgibt.Dein Vorhaben sieht dann so aus (eine Stelle die du definiert hast finde ich zwar nicht aber das solltest du dann selber sehen). In die Werte habe ich nur durch Demo-Text gesetzt, das kannst du dann durch den Inhalt deiner Array-Werte ersetzen.
# xml Objekt erstellen
$xml = New-Object XML
# xml laden
$xml.Load('D:\spsman\demo.xml')
# NamespaceManager erstellen
[System.Xml.XmlNamespaceManager]$ns = new-Object System.Xml.XmlNamespaceManager $xml.NameTable
# Extract all used Namespaces in XML document and them to NamespaceManager
$xml.SelectNodes('//*') | ?{$_.NamespaceURI -ne ''} | select @{n='Prefix';e={$_.GetPrefixOfNamespace($_.NamespaceURI)}},NamespaceURI | select Prefix,NamespaceURI -unique | %{if($_.Prefix -eq ''){$_.Prefix = 'default'};$ns.AddNamespace($_.Prefix,$_.NamespaceURI)}
# Abfrage des ersten 'SW.Blocks.CompileUnit' Knotens
$node = $xml.SelectSingleNode("//SW.Blocks.CompileUnit")
# abbrechen wenn kein Knoten gefunden wurde.
if (!$node){
throw "Error, 'SW.Blocks.CompileUnit' Node not found!"
exit 1
}
# erstelle eine Kopie des Knotens im Speicher
$newnode = $node.Clone()
# id anpassen
$newnode.Id = "100"
# OPN anpassen
$newnode.SelectNodes("//default:Component[@Name='OPN_SOLL']",$ns) | %{$_.setAttribute('Name','V15_SOLL')}
# NW_KOMMENTAR anpassen
$newnode.SelectSingleNode("//ObjectList/MultilingualText[@CompositionName='Title']//AttributeList[Culture='de-DE']/Text") | %{$_.innerText = "Whatever"}
# hänge den kopierten Knoten im selben Parent wie vom Original wieder ins XML ein
[void]$node.ParentNode.AppendChild($newNode)
# speichere das geänderte xml unter neuem Namen
$xml.Save('D:\spsman\demo_out.xml')
Alternativ kannst du Knoten auch wenn die Position im Baum fest ist auch ohne XPath Selection ansprechen, bspw. für die Variable $node in obigem Skript sähe das dann so aus um den ersten 'SW.Blocks.CompileUnit' Knoten zu bekommen
$node = $xml.Document.'SW.Blocks.FC'.ObjectList.'SW.Blocks.CompileUnit'
Grüße Uwe
Servus again.
Kannst du leicht addressieren
Man sollte vorher nochmal Korrektur lesen, erneuter Tippfehler ... Compnent
Ich bin jetzt raus. Da kommt man sich hier doch ehrlich gesagt langsam aber sicher veralbert vor wenn man hier alles schnell mal einkippt bevor man es überhaupt Korrektur gelesen hat ... .
Bitte lies dir zur XPATH-Selections Syntax die einschlägigen Dokumentationen durch und übe damit sonst lernst du aus dem ganzen hier nichts.
https://www.w3schools.com/xml/xpath_syntax.asp
Und vor allem, mehr Präzision an den Tag legen!
Viel Erfolg.
Grüße Uwe
p.s. Das ursprüngliche Problem dieses Threads wurde gelöst, alles andere ist jetzt Off-Topic und kannst du dir gerne per PN von mir ein Angebot für weitere Arbeiten einholen!
Den Beitrag also bitte noch auf gelöst setzen, und Lösungen markieren. Merci.
Zitat von @SPSman:
" <Component Name="OPN" />" wird nicht angepasst bzw. selectnodes läuft mal wieder ins leere..
War ja auch keine Anforderung deinerseits oben, da war die Rede von "OPN_SOLL"." <Component Name="OPN" />" wird nicht angepasst bzw. selectnodes läuft mal wieder ins leere..
Dann muss der Bereich hinter SW.Blocks.CompileUnitauch wieder dahinter eingefügt werden
Macht mein Skript mit deinem oben geposteten XML einwandfrei.Und ALLE "ID"(nicht Uid) Atribute müssen verändert werden.
Kannst du leicht addressieren
$xml.SelectNodes("//*[@ID]")
Es sollen alle Attribute die "OPN*" im Namen enthalten durch eine String Variable ersetzt werden.
Wie kann ich alle Nodes im $NewNode die den String "OPN"(also Sowohl "OPN" als auch "OPN_Soll" oder "OPN bla") adressieren?
Select sieht dann so ausWie kann ich alle Nodes im $NewNode die den String "OPN"(also Sowohl "OPN" als auch "OPN_Soll" oder "OPN bla") adressieren?
$xml.SelectNodes("//*[contains(@*,'OPN')]")
Wie Kann ich alle Nodes im $NewNode die eine "ID" haben heraussuchen?
s. oben.Wieso $Temp_Kommentar nicht geschrieben?
Tippfehler der Variable $Temp_Komentar statt $Temp_Kommentar$newnode.SelectNodes("//default:**Compnent**[@Name='OPN']",$ns) | %{$_.SetAttribute('Name',$Obj+" ")}#< geht nicht
Man sollte vorher nochmal Korrektur lesen, erneuter Tippfehler ... Compnent
Ich bin jetzt raus. Da kommt man sich hier doch ehrlich gesagt langsam aber sicher veralbert vor wenn man hier alles schnell mal einkippt bevor man es überhaupt Korrektur gelesen hat ... .
Bitte lies dir zur XPATH-Selections Syntax die einschlägigen Dokumentationen durch und übe damit sonst lernst du aus dem ganzen hier nichts.
https://www.w3schools.com/xml/xpath_syntax.asp
Und vor allem, mehr Präzision an den Tag legen!
Viel Erfolg.
Grüße Uwe
p.s. Das ursprüngliche Problem dieses Threads wurde gelöst, alles andere ist jetzt Off-Topic und kannst du dir gerne per PN von mir ein Angebot für weitere Arbeiten einholen!
Den Beitrag also bitte noch auf gelöst setzen, und Lösungen markieren. Merci.
Zitat von @colinardo:
...
Ich bin jetzt raus. Da kommt man sich hier doch ehrlich gesagt langsam aber sicher veralbert vor wenn man hier alles schnell mal einkippt bevor man es überhaupt Korrektur gelesen hat ... .
...
Ich bin jetzt raus. Da kommt man sich hier doch ehrlich gesagt langsam aber sicher veralbert vor wenn man hier alles schnell mal einkippt bevor man es überhaupt Korrektur gelesen hat ... .
Das Problem, kann man mit Python lösen.
Monty Python
Zitat von @SPSman:
Das ist nicht Korrekt. Oben schrieb ich Zeile 8 und Zeile 16 soll verändert werden. (OPN_SOLL und OPN)
Das ist nicht Korrekt. Oben schrieb ich Zeile 8 und Zeile 16 soll verändert werden. (OPN_SOLL und OPN)
Da grätsch ich doch mal dazwischen ... hast du oben bei der 16 wohl ne 1 vergessen. In Zeile 6 findet man da nix mit "OPN" :-P.
:
Vielleicht solltest du beim Tippen mal die Fäustlinge ausziehen 🙈 .
Wer weiß das schon, wenn deine Tasten 10x10cm groß sind dann wahrscheinlich nicht, außer du hast kein Zielwasser getrunken