Given xml of
$xml = [Xml]@"
<Package>
<Copy_Ex>
</Copy_Ex>
<Move_Ex>
<Rules>
<Rule>
<Property>os</Property>
<Operator>-eq</Operator>
<Value>Windows10</Value>
</Rule>
</Rules>
<Task>
<Rules>
<Rule>
<Property>lastWriteTime</Property>
<Operator>-gt</Operator>
<Value>6W</Value>
</Rule>
</Rules>
</Task>
</Move_Ex>
</Package>
"@
I want to be able to select a single node from the xml, say <Move_Ex>, and pass it to a function, then independently access the two <Rules> nodes. I can do that now with
function Test {
param (
[Xml.XmlElement]$task
)
if ($nodeRules = $task.SelectSingleNode('//Rules')) {
Write-PxXmlToConsole $nodeRules
}
if ($taskRules = $task.SelectSingleNode('//Task/Rules')) {
Write-PxXmlToConsole $taskRules
}
}
$task = $xml.SelectSingleNode('//Move_Ex')
Test $task
But // is searching the entire document, NOT just the [Xml.XmlElement] that I passed. So if I have a different <Rules> node in the <Copy_Ex> node, THAT is what gets returned by the first SelectSingleNode(). What I think I need is a way to identify the root node of the passed element, not the entire document. But I can't seem to find a way to do that consistently. My understanding is that while // finds the sequence of nodes anywhere in the document, / only finds it relative to the root node. Which should mean that
$task = $xml.SelectSingleNode('/Move_Ex')
finds only that <Move_Ex> in the root, and if I had another one somewhere else I would be fine. However, that doesn't return anything at all, which has me worried I don't really understand how either / or // works, which makes the chances of getting what I need to work unlikely.
I have looked at the documentation for [Xml.XmlElement] and GetElementsByTagName() seems to be finding just the elements in my passed element. Write-Host "$($task.GetElementsByTagName('Rules').Count)" in the function returns a 2, so not finding any <Rules> node I have put in <Copy_Ex>.
I also tried just dot referencing things, so
Write-PxXmlToConsole $task.Rules
Write-PxXmlToConsole $task.Task.Rules
And that seems to be working also. But again, ONLY when the element being passed is selected with // rather than /, and I worry that in a much more complex XML with hundreds of <Rules> nodes I won't be getting consistent results.
So, two questions...
1: What is the difference between / and // in this situation, and why isn't /Move_Ex working as "expected" in $task = $xml.SelectSingleNode('/Move_Ex')?
2: Is the dot referencing approach the "correct" way to limit my access to just the passed Element? Or is there another/better way?
I should note here that I did verify that // does break down if make the XML more realistic. So with this XML
$xml = [Xml]@"
<Package>
<Copy_Ex>
<Rules>
<Rule>
<Property>os</Property>
<Operator>-eq</Operator>
<Value>WindowsXP</Value>
</Rule>
</Rules>
<PreTask>
<Move_Ex>
<Rules>
<Rule>
<Property>os</Property>
<Operator>-eq</Operator>
<Value>WindowsVista</Value>
</Rule>
</Rules>
<Task>
<Rules>
<Rule>
<Property>lastWriteTime</Property>
<Operator>-gt</Operator>
<Value>8W</Value>
</Rule>
</Rules>
</Task>
</Move_Ex>
</PreTask>
</Copy_Ex>
<Move_Ex>
<Rules>
<Rule>
<Property>os</Property>
<Operator>-eq</Operator>
<Value>Windows10</Value>
</Rule>
</Rules>
<Task>
<Rules>
<Rule>
<Property>lastWriteTime</Property>
<Operator>-gt</Operator>
<Value>6W</Value>
</Rule>
</Rules>
</Task>
</Move_Ex>
</Package>
"@
Where I need to select only the <Move_Ex> in the <Package> node
$task = $xml.SelectSingleNode('/Move_Ex')
selects nothing, while
$task = $xml.SelectSingleNode('//Move_Ex')
selects the <Move_Ex> node nested in <Copy_Ex>, which is not at all what I want and need.
Note that all Write-PxXmlToConsole does is exactly that.
function Write-PxXmlToConsole ($xml) {
$stringWriter = New-Object System.IO.StringWriter
$xmlWriter = New-Object System.Xml.XmlTextWriter $stringWriter
$xmlWriter.Formatting = "indented"
$xml.WriteTo($xmlWriter)
$xmlWriter.Flush()
$stringWriter.Flush()
Write-Host $stringWriter.ToString()
Write-Host
}
EDIT: With respect to my first question, and based on what @Prophet has already said, I realized I was conflating Root NODE and Root ELEMENT, and now I see this in my related links. Some good additional info about Root node, root element, document element, etc.