21

The scenario: I'm using Select-Object to access properties of a piped object, and one of those properties is itself an object. Let's call it PropertyObject. I want to access a property of that PropertyObject, say Property1. Is there any nice and clean way of accessing Property1, along the lines of:

...| select-object PropertyObject.Property1

While experimenting I can only get it to work if I do something like:

...| select-object {$_.PropertyObject.Property1}

and if I want to display it with a decent column name it gets even messier:

...| select-object @{Name="Property1"; Expression={$_.PropertyObject.Property1}}

Given how clean and concise PowerShell is in general, I can't help thinking I'm missing something and there should be a cleaner way of accessing a property of a property. Is there?

EDIT: As requested by Matt, here is the concrete example:

I'm reading an XML file, Books3.xml:

<?xml version="1.0"?>
<catalog>
   <book id="bk101">
      <author>Gambardella, Matthew</author>
      <title>XML Developer's Guide</title>
      <genre>Computer</genre>
      <price>44.95</price>
      <publish_date inprint="false">2000-10-01</publish_date>
      <description>An in-depth look at creating applications 
      with XML.</description>
      <publisher>
        <name>Simon and Schuster</name>
        <country>USA</country>
        <city>New York</city>
      </publisher>
   </book>
   <book id="bk102">
      <author>Ralls, Kim</author>
      <title>Midnight Rain</title>
      <genre>Fantasy</genre>
      <price>5.95</price>
      <publish_date inprint="true">2000-12-16</publish_date>
      <description>A former architect battles corporate zombies, 
      an evil sorceress, and her own childhood to become queen 
      of the world.</description>
      <publisher>
        <name>HarperCollins</name>
        <country>USA</country>
        <city>New York</city>
      </publisher>
   </book>
   <book id="bk103">
      <author>Corets, Eva</author>
      <title>Maeve Ascendant</title>
      <genre>Fantasy</genre>
      <price>5.95</price>
      <publish_date inprint="false">2000-11-17</publish_date>
      <description>After the collapse of a nanotechnology 
      society in England, the young survivors lay the 
      foundation for a new society.</description>
      <publisher>
        <name>Macmillan</name>
        <country>United Kingdom</country>
        <city>London</city>
      </publisher>
   </book>
</catalog>

Code to load XML into XmlDocument:

$filePath = "C:\Temp\books3.xml"
$xmlDoc = new-object xml
$xmlDoc.load($filePath)

Attempting to read details for each book:

$xmlDoc.catalog.book | select author, title, publisher.name

Result:

author                     title                      publisher.name
------                     -----                      --------------
Gambardella, Matthew       XML Developer's Guide
Ralls, Kim                 Midnight Rain
Corets, Eva                Maeve Ascendant
Simon Elms
  • 17,832
  • 21
  • 87
  • 103
  • 1
    What is your powershell version? If its 3.0 you can just `$object.PropertyObject.Property1` else you would just chain the selects `select PropertyObject | select Property1`. You might need some expands in there but that is the jist. It all depends on the props – Matt Apr 12 '15 at 23:20
  • I made a minor update but I don't think it is the answer you are looking for. Extracting a single property is easier than the object output you are looking for. `Select` is the way to go for this. You could clean it up by moving the hash to a single variable but it does not change the code needed. – Matt Apr 13 '15 at 03:33

3 Answers3

48

You can use calculated properties. A short form would allow for this:

$xmlDoc.catalog.book | select author, title, {$_.publisher.name}

If the property names are important you need to add them in.

$xmlDoc.catalog.book | select author, title, @{N="PublisherName";E={$_.publisher.name}}

Where N is short for name and E is short for expression.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
khaledh
  • 1,047
  • 1
  • 10
  • 17
8

It would be easier if you had something actual repeatable for us to test but we can fake that with Get-Items return.

(Get-Item C:\temp\logoutput).Parent.Name

.Parent is actually a System.IO.DirectoryInfo object. I use PowerShell 3.0 dot notation to get the name of the parent

The same result can be acquired by chaining the select calls

Get-Item C:\temp\logoutput | select -expand Parent | Select -Expand name

This of course would work in PowerShell 2.0 but is not a terse and the 3.0 version.

Post Question Edit

Not sure what you are hoping for. What you have does work at extracting sub properties the way you have it. I can only offer an alternate approach that might be more intuitive and friendly but the result is the same.

$xml.catalog.book | ForEach-Object{
    $_ | Add-Member NoteProperty "PublisherName" $_.publisher.name -PassThru}

Of course you might still need to use select to get your output restricted to the properties you need but it is another option.

Matt
  • 45,022
  • 8
  • 78
  • 119
  • 1
    I think your Post Question-Edit answers my question: I can't do something like `select author, title, publisher.name` and I have to do something a bit more complicated to get the publisher name. I'm learning what does and doesn't work with PowerShell and that's really what I wanted to find out. Cheers. – Simon Elms Apr 13 '15 at 06:18
1

I too was on the hunt for how to accomplish a select of an object's sub-property. In my case it was in order to export a DNS Server's Zone data. I'll share the guts on what I used to accomplish my goal and this posting here helped!

To do what you were looking for specifically try modifying your code as follows:

$xmlDoc.catalog.book | Select Author, Title, -Expand Publisher | Select Author, Title, Name | Sort Author | FT -auto

Based on the table output you provided the above should work. It worked for me which you'll see below.

This script worked great for what I needed and is a blend of different ideas. Hopefully it works for someone else.

$Zones = @(Get-DnsServerZone)
ForEach ($Zone in $Zones) {
    Write-Host "`n$($Zone.ZoneName)" -ForegroundColor "Yellow"
    $Data = New-Object System.Object
    $Data = $Zone | Get-DnsServerResourceRecord
    $Data | Add-Member -MemberType NoteProperty -Name "ZoneName" -Value $Zone.ZoneName
    $Data += $Data
    $Data | Select-Object ZoneName, HostName, RecordType -Expand RecordData | Select ZoneName, HostName, RecordType, IPv4Address, HostNameAlias, NameServer, PrimaryServer, ExpireLimit, MinimumTimeToLive, RefreshInterval, ResponsiblePerson, RetryDelay, SerialNumber, DomainName, Port, Priority, Weight | Sort RecordType, HostName | Export-Csv -NoTypeInformation "$($Zone.ZoneName).csv"
}

The Yellow Foreground is handy when you're looking at the output prior to export. Just put the } right after HostName and before | Export-Csv like this: ... Sort RecordType, HostName }

Shad
  • 11
  • 2
  • Tried it but got the following error: `Select-Object : A positional parameter cannot be found that accepts argument 'publisher'.` I wonder if it's because the book object is of type System.Xml.XmlElement? – Simon Elms Dec 16 '16 at 04:04