Thursday, May 28, 2009

Iterating nodes with the XML handler

Say you've got the following XML file:

<Root>
<Child />
<Child />
</Root>

Run this:

PS > $myDoc = [xml](Get-Content $pathToMyDoc)
PS
> @($myDoc.SelectNodes("/Root/Child")).Count
2
PS
> @($myDoc.Root.Child).Count
2

Now edit the XML file so it has no Child nodes, just the Root node, and run those statements again:

PS > $myDoc = [xml](Get-Content $pathToMyDoc)
PS
> @($myDoc.SelectNodes("/Root/Child")).Count
0
PS
> @($myDoc.Root.Child).Count
1

That 1 is annoying when you want to iterate over a collection of nodes using foreach if and only if there actually are any. This is how I learned that you cannot use the XML handler's property (dot) notation as a simple shortcut. I believe what's happening is that SelectNodes returns a collection of 0. When @'ed, it is transformed from an XPathNodeList to an Object[] (check GetType()), but the length is preserved because @ intelligently handles arrays. The dynamically generated $myDoc.Root.Child property (which essentially does not exist) returns $null. When $null is @'ed, it becomes an array of length 1.