Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Powershell - Not all XML attributes are getting passed down pipeline

Tags:

powershell

xml

I am writing a fairly substantial Posh module to be a CLI front-end to a set of RESTful Web Services. I have an odd problem I have been able to characterize, but not finding a clean solution to.

I do not always know what attributes to expect, and some nodes may have a certain attribute, other nodes may not have that attribute. My problem is sometimes (read: often) if an attribute is not present in all nodes, it will not be passed down the pipeline for any node. It appears to be related to what attributes are in the first node in the XML.

For a very simple example, consider this is the contents of an XML file example.xml:

<members>
    <member>
        <name>Joe</name>
        <rank>Grand Poobah</rank>
        <serialnumber>1234</serialnumber>
    </member>
    <member>
        <name>Fred</name>
        <serialnumber>1234</serialnumber>
    </member>
 </members>

I import it and assign it to an XML cast variable:

[xml]$stuff = Get-Content .\example.xml

Then I pipe it to format-table (or out-gridview, export-csv, etc.)

$stuff.members.member | ft -AutoSize

name  rank          serialnumber
----  ----         ------------
Joe   Grand Poobah  1234
Fred                1234

Works great.

Now, if the FIRST node does not have the rank attribute:

<members>
    <member>
        <name>Fred</name>
        <serialnumber>1234</serialnumber>
    </member>
    <member>
        <name>Joe</name>
        <rank>Grand Poobah</rank>
        <serialnumber>1234</serialnumber>
    </member>
 </members>

Now, when I do the same import and pass it out the pipe, the "rank" attribute is not passed down the pipeline.

$stuff.members.member | ft -AutoSize

name   serialnumber
----   ------------
Fred   1234
Joe    1234

I can force it by explicitly naming the attributes:

$stuff.members.member | select-object -Property name, rank, serialnumber | ft

name  rank          serialnumber
----  ----          ------------
Fred                1234
Joe   Grand Poobah  1234

Explicitly naming the attributes is a recipe for bugs, as attributes can be added without warning. I want to keep the code flexible to deal with new attributes if possible.

I thought I could use "select-object -property *" but that adds a lot of PowerShell inserted XML stuff I do not want, and still does not pipe the rank attribute.

$stuff.members.member | select-object -Property *| ft -AutoSize
name serialnumber LocalName NamespaceURI Prefix NodeType ParentNode OwnerDocument IsEmpty Attributes
---- ------------ --------- ------------ ------ -------- ---------- ------------- ------- ----------
Fred 1234         member                         Element members    #document       False {}
Joe  1234         member                         Element members    #document       False {}

Anyone familiar with any good ways to deal with this situation? I have not found any good clues on a Google search.

like image 799
user2678909 Avatar asked Aug 13 '13 15:08

user2678909


1 Answers

The XML element definitely is "passed down the pipe". What you are looking at is a peculiarity of format-table: It only looks at the first object in a list to figure out which columns to print.

This mainly is for performance reasons. Otherwise format-table would have to iterate the list twice - once to figure out which properties to output, and a second time to actually output them. Plus, more often than not, items in a list are uniform.

In all other cases, the rule "explicit is better than implicit" applies:

$stuff.members.member | ft name, rank, serialnumber -AutoSize
like image 186
Tomalak Avatar answered Oct 19 '22 07:10

Tomalak