Passing Objects as properties of Custom PowerShell Objects through the pipeline

This topic contains 2 replies, has 2 voices, and was last updated by Profile photo of Curtis Smith Curtis Smith 1 year, 7 months ago.

  • Author
    Posts
  • #29164
    Profile photo of Curtis Smith
    Curtis Smith
    Participant

    Hello all. I am trying to understand the below behavior. The below code is very simple and made just to present the behavior, not to be useful code.

    First I have a function that generated a collection of Custom PSObjects and then uses that collection as a property for another Custom PSObject that is sent out to the pipeline along with 1 string property and 1 DateTime Object property.

    function Test1 {
        $collection = @()
        foreach ($i in 0..10) {
            $collection += New-Object -TypeName PSObject -Property @{value1 = "1:$i"; value2 = "2:$i"; value3 = "3:$i"}
        }#foreach
    
        New-Object -TypeName PSObject -Property @{name = "Collection"; value = $collection; date = (Get-Date)}
    }#function
    

    The result of this function to standard output is exactly what I would expect. A date property with the date, a name property with a string of "Collection", and a value property with a collection of values


    PS C:\> test1

    date name value
    ---- ---- -----
    8/31/2015 10:02:45 PM Collection {@{value1=1:0; value2=2:0; v...

    I then have a second function that I want to accept the object that has been output by the first function as input for the second function.

    function Test2 {
        [CmdletBinding()]
        Param (
            [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
            [String]$Name,
            [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
            $Value,
            [Parameter(Mandatory=$True, ValueFromPipeline=$True, ValueFromPipelineByPropertyName=$True)]
            $Date
        )
        Begin{}
        Process {
            Write-Host "----------------------------------------------------"
            Write-Host $Name
            Write-Host "----------------------------------------------------"
            Write-Host $Value
            Write-Host "----------------------------------------------------"
            Write-Host $Date
            Write-Host "----------------------------------------------------"
            Write-Host $Value.Value
            Write-Host "----------------------------------------------------"
            Write-Host $Date.Date
            Write-Host "----------------------------------------------------"
        }#process
        End{}
    }#function
    

    Below are the results of piping test1 to test2 and where I become confused.


    PS C:\> test1 | test2
    ----------------------------------------------------
    Collection
    ----------------------------------------------------
    @{date=8/31/2015 10:07:32 PM; name=Collection; value=System.Object[]}
    ----------------------------------------------------
    @{date=8/31/2015 10:07:32 PM; name=Collection; value=System.Object[]}
    ----------------------------------------------------
    @{value1=1:0; value2=2:0; value3=3:0} @{value1=1:1; value2=2:1; value3=3:1} @{value1=1:2; value2=2:2; value3=3:2} @{value1=1:3; value2=2:3; value3=3:3} @{value1=1:4; value2=2:4; value3=3:4} @{value1=1:5; value2=2:5; value3=3:5} @{value1=1:6; value2=2:6; value3=3:6} @{value1=1:7; value2=2:7; value3=3:7} @{value1=1:8; value2=2:8; value3=3:8} @{value1=1:9; value2=2:9; value3=3:9} @{value1=1:10; value2=2:10; value3=3:10}
    ----------------------------------------------------
    8/31/2015 10:07:32 PM
    ----------------------------------------------------

    You can see by the results that $data and $value seem to contain the complete object from the pipeline rather than just the value that they were set to by the new-object in test1. This is confirmed by the fact that I can get the property I want from the variable that should contain the property value by using ..

    Lastly while I am very interested to know why this behaves in this manner, the ultimate goal is to do things correctly. Since this is the behavior, it is obviously not the way to handle this scenario. So what is the correct methodology for passing a collection of values as a property that can be passed through the pipeline to another function?

    Thanks

  • #29165
    Profile photo of Peter Jurgens
    Peter Jurgens
    Participant

    The solution is to remove the "ValueFromPipeline=$True". When parameter binding occurs it tries to bind parameters by value first. Since your Value and Date parameters are not strongly typed, and you are enabling value from pipeline, it will bind the whole object to the parameter.

    The other solution would be to strongly type your parameters, which works for date eg
    [DateTime]$Date

    However, since $Value would be a PSCustomObject, and the parent object that you're passing down the pipeline is also a PSCustomObject, this will still bind the whole object to the $Value parameter rather than just the $_.Value object.

  • #29170
    Profile photo of Curtis Smith
    Curtis Smith
    Participant

    Peter, thanks that is exactly what I needed. A little further investigation and I found that I can get the correct data by both dropping the "ValueFromPipeline=$True" parameter, and by strong typing my collection as [Object]. I did try strong typing previously, however since I had ValueFromPipeline=$True still defined, I got the same result for the Custom PowerShell Object. The below changes to the Test2 function now work as expected.

    function Test2 {
        [CmdletBinding()]
        Param (
            [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
            [String]$Name,
            [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
            [Object]$Value,
            [Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
            [DateTime]$Date
        )
        Begin{}
        Process {
            Write-Host "----------------------------------------------------"
            Write-Host $Name
            Write-Host "----------------------------------------------------"
            Write-Host $Value
            Write-Host "----------------------------------------------------"
            Write-Host $Date
            Write-Host "----------------------------------------------------"
            Write-Host $Value.Value
            Write-Host "----------------------------------------------------"
            Write-Host $Date.Date
            Write-Host "----------------------------------------------------"
        }#process
        End{}
    }#function
    


    PS C:\Users\csmith> Test1 | Test2
    ----------------------------------------------------
    Collection
    ----------------------------------------------------
    @{value1=1:0; value2=2:0; value3=3:0} @{value1=1:1; value2=2:1; value3=3:1} @{value1=1:2; value2=2:2; value3=3:2} @{value1=1:3; value2=2:3; value3=3:3} @{value1=1:4; value2=2:4; value3=3:4} @{value1=1:5; value2=2:5; value3=3:5} @{value1=1:6; value2=2:6; value3=3:6} @{value1=1:7; value2=2:7; value3=3:7} @{value1=1:8; value2=2:8; value3=3:8} @{value1=1:9; value2=2:9; value3=3:9} @{value1=1:10; value2=2:10; value3=3:10}
    ----------------------------------------------------
    9/1/2015 9:54:12 AM
    ----------------------------------------------------

    ----------------------------------------------------
    9/1/2015 12:00:00 AM
    ----------------------------------------------------

You must be logged in to reply to this topic.