More PowerShell, XML and XPath -- Select-Xml and multi-node selections
Let's start with the same document as before.
<StyleCopSettings Version="4.3"> | |
<Analyzers> | |
<Analyzer AnalyzerId="Microsoft.StyleCop.CSharp.DocumentationRules"> | |
<Rules> | |
<Rule Name="FileMustHaveHeader"> | |
<RuleSettings> | |
<BooleanProperty Name="Enabled">False</BooleanProperty> | |
</RuleSettings> | |
</Rule> | |
<Rule Name="FileHeaderMustShowCopyright"> | |
<RuleSettings> | |
<BooleanProperty Name="Enabled">False</BooleanProperty> | |
</RuleSettings> | |
</Rule> | |
</Rules> | |
<AnalyzerSettings /> | |
</Analyzer> | |
</Analyzers> | |
</StyleCopSettings> |
Now at PowerShell 2, we can also do
>$n = Select-Xml -Xml $x -XPath "//Rule[@Name='FileHeaderMustShowCopyright']//BooleanProperty[@Name='Enabled']" | |
>$n | |
Node Path Pattern | |
---- ---- ------- | |
BooleanProperty InputStream //Rule[@Name='FileHeaderMustShowCopy... | |
>$n.GetType() | |
IsPublic IsSerial Name BaseType | |
-------- -------- ---- -------- | |
True False SelectXmlInfo System.Object | |
>$n.Node | |
Name #text | |
---- ----- | |
Enabled False | |
>$n.Node.ParentNode.ParentNode | |
Name RuleSettings | |
---- ------------ | |
FileHeaderMustShowCopyright RuleSettings |
which avoids the explicit call to the .net infrastructure, but wraps up the actual content we got in the previous example as the Node
field inside an object. This is not so much of a problem when picking a single node, but when you want to do the equivalent of SelectNodes
nested (e.g. for each Rule, do something with each setting) some disassembly is required in order to perform the inner selection:
>$s = Select-Xml -Xml $x -XPath '//Rule' | |
>$s | foreach-object { $_.Node ; Select-Xml $_.Node -XPath 'descendant::BooleanProperty' | foreach-object { $_.Node } } | |
Name RuleSettings | |
---- ------------ | |
FileMustHaveHeader RuleSettings | |
Enabled | |
FileHeaderMustShowCopyright RuleSettings | |
Enabled | |
compare | |
>$nn = $x.SelectNodes("//Rule") | |
>$nn | ForEach-Object { $_ ; $_.SelectNodes("descendant::BooleanProperty") } | |
Name RuleSettings | |
---- ------------ | |
FileMustHaveHeader RuleSettings | |
Enabled | |
FileHeaderMustShowCopyright RuleSettings | |
Enabled |
It is, however, a bit of an oversight that the Select-Xml
cmdlet doesn't consume SelectXmlInfo
objects through the usual type conversion mechanisms inside the cmdlet infrastructure:
>$s | foreach-object { Select-Xml -xml $_ -XPath 'descendant::BooleanProperty' | foreach-object { $_.Node } } | |
Select-Xml : Cannot bind parameter 'Xml'. Cannot convert the "<RuleSettings><BooleanProperty Name="Enabled">False</Bool | |
eanProperty></RuleSettings>" value of type "Microsoft.PowerShell.Commands.SelectXmlInfo" to type "System.Xml.XmlNode". | |
At line:1 char:38 | |
+ $s | foreach-object { Select-Xml -xml <<<< $_ -XPath '//BooleanProperty' | foreach-object { $_.Node } } | |
+ CategoryInfo : InvalidArgument: (:) [Select-Xml], ParameterBindingException | |
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.SelectXmlCommand | |
Select-Xml : Cannot bind parameter 'Xml'. Cannot convert the "<RuleSettings><BooleanProperty Name="Enabled">False</Bool | |
eanProperty></RuleSettings>" value of type "Microsoft.PowerShell.Commands.SelectXmlInfo" to type "System.Xml.XmlNode". | |
At line:1 char:38 | |
+ $s | foreach-object { Select-Xml -xml <<<< $_ -XPath '//BooleanProperty' | foreach-object { $_.Node } } | |
+ CategoryInfo : InvalidArgument: (:) [Select-Xml], ParameterBindingException | |
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.SelectXmlCommand |
but once you know there's that gotcha, it can be worked around.
No comments :
Post a Comment