Reputation: 57
I have the following to dynamically expand properties of objects I feed into the function, but ToString() is often spitting out the property types instead:
Function ExpandMultivaluedProperties
{
Param(
[PSObject]$InputObject
)
$results= $InputObject |
ForEach-Object {
$properties = New-Object PSObject
$_.PSObject.Properties |
ForEach-Object {
$propertyName = $_.Name
$propertyValue = $_.Value
If ($propertyValue -NE $NULL) {
$values = @()
ForEach ($value In $propertyValue) {
$values += $value.ToString()
}
Add-Member -inputObject $properties NoteProperty -name $propertyName -value "$([String]::Join(";",$values))"
} Else {
Add-Member -inputObject $properties NoteProperty -name $propertyName -value $NULL
}
}
$properties
}
return $results
}
ExpandMultivaluedProperties -InputObject (Get-ExchangeCertificate) | Export-CSV -path "Cert.csv" -NoTypeInformation
In particular with the results of Get-ExchangeCertificate, what I end up with is
System.Security.Cryptography.X509Certificates.X500DistinguishedName
as the IssuerName for each certificate.
The code works fine for any string-friendly properties, but I'm aiming for it to be robust enough to handle any property that would normally show up correctly with a | Format-List
Any thoughts on how to print/expand similar properties programmatically without having to use a "Select" expression?
Upvotes: 1
Views: 619
Reputation: 437998
In a string context, the instances of many types default to simply printing their type name - e.g., System.Security.Cryptography.X509Certificates.X500DistinguishedName
- which is often not helpful.
However, you can use Out-String
instead, which applies PowerShell's default output formatting (the same output format you'd see in the console):
$values += Out-String -InputObject $value
One caveat is that the resulting strings often have leading or trailing empty lines. You can remove leading and/or trailing empty lines with the following variation:
$values += (Out-String -InputObject $value).Trim()
Caveats:
The default width of the output lines for non-string data is based on the PowerShell host, and defaults to the width of the window buffer in the regular console minus 1 (excluding the line break); i.e., 79
in with the default console window size.
Use -Width
to specify an output line width (excluding the line break) explicitly.
Longer lines are truncated, i.e., the information is lost.
[string]
values are not affected - they are output as-is.Conversely, if the output is in table format (implicit use of Format-Table
), shorter lines are right-padded with spaces to the implied or specified width.
Given that lines can be padded, you shouldn't use something like Out-String -Width ([int]::MaxValue])
to prevent truncation (in fact, you may run out of memory).
Instead, use a reasonably high value such as -Width 255
, as @Abraxas000 did in his own answer.
If you wanted to condense the typically multi-line output to a one-liner, use something like the the following, though :
$values += ($value | Out-String).Trim() -replace '\s*\r?\n', '; ' -replace '\s+', ' '
;
was chosen to replace the line breaks here, and line-internal runs of whitespace are normalized to a single space each - whether the results are still readable probably both depends on the specific output format and the eye of the beholder.
To give a concrete example:
> $ht = @{ one = 1; two = 2; three = 3 } # sample hashtable
> $val = "$ht"; "ht: $val" # hashtable is stringified -> type name only
ht: System.Collections.Hashtable
> $val = Out-String -InputObject $ht; "ht: $val" # Out-String creates meaningful representation
ht:
Name Value
---- -----
one 1
two 2
three 3
> ($ht | Out-String).Trim() -replace '\s*\r?\n', '; ' -replace '\s+', ' '
Name Value; ---- -----; one 1; two 2; three 3
Upvotes: 3
Reputation: 57
@mklement0 Gave me an answer that lead me as close to ideal as I have time for, so go up-vote it!
My code as of posting this:
Function ExpandMultivaluedProperties
{
Param(
[PSObject]$InputObject
)
$results= $InputObject |
ForEach-Object {
$properties = New-Object PSObject
$_.PSObject.Properties |
ForEach-Object {
$propertyName = $_.Name
$propertyValue = $_.Value
If ($propertyValue -NE $NULL) {
$values = @()
ForEach ($value In $propertyValue) {
if (($value.ToString()).StartsWith("System."))
{
$values += (Out-String -InputObject $value -Width 255).Trim()
}
else
{
$values += $value.ToString()
}
}
Add-Member -inputObject $properties NoteProperty -name $propertyName -value "$([String]::Join(";",$values))"
} Else {
Add-Member -inputObject $properties NoteProperty -name $propertyName -value $NULL
}
}
$properties
}
return $results
}
This gives me the one-line value I expect to see for the simpler properties while properly expanding the complex properties.
Even if the property is simple and the value starts with "System.", the data will still be present, just not in the simpler, one-liner format I prefer for CSVs.
Thanks again, @mklement0!
Upvotes: 1