PowerShell object representation

208 views Asked by At

There are different ways to represent (display) an object in PowerShell but when it comes to complex objects it is either very verbose or quiet useless:

$Object = @{ 'a' = 1; 'b' = 2 }

Just outputting (Format-Host, or Format-List) returns a multiline output, as:

$Object

Name                           Value
----                           -----
a                              1
b                              2

Converting it to a string "$Object" or $Object.ToString() doesn't reveal anything about its contents:

System.Collections.Hashtable

Converting it to Json $Object |ConvertTo-Json -Compress, gets close to what I would like to see (but a little too expressive cmdlet for what I need):

{"a":1,"b":2}

I wonder whether there is a way access the representation formatting used by PowerShell display cmdlets, as e.g. the value behind the ImmediateBaseObject and BaseObject:

$Object.PSObject

Members             : {System.Object Item(System.Object key) {get;set;}, bool IsReadOnly {get;}, bool IsFixedSize {get;}, bool IsSynchronized {get;}…}
Properties          : {bool IsReadOnly {get;}, bool IsFixedSize {get;}, bool IsSynchronized {get;}, System.Collections.ICollection Keys {get;}…}
Methods             : {Add, Clear, Clone, Contains…}
ImmediateBaseObject : {[a, 1], [b, 2]}                     # <-- This representation
BaseObject          : {[a, 1], [b, 2]}                     #
TypeNames           : {System.Collections.Hashtable, System.Object}

Wishful thinking, I would like to be able to do something like this:

[PowerShellFormat]$Object

And get the same formatting back for any (complex) object as shown behind BaseObject for $Object.PSObject.
As for this case:

{[a, 1], [b, 2]}
2

There are 2 answers

0
mklement0 On BEST ANSWER

JosefZ has provided the crucial pointer:

  • Call .GetEnumerator() on your dictionary in order to enumerate its entries, which are key-value pairs, such as of type [System.Collections.DictionaryEntry] in hashtable instances.

  • Unlike dictionaries as a whole, their entries stringify meaningfully, in the form [<key>, <value>] - though it's important to note that this is just a for-display representation, not suitable for programmatic processing.

  • When PowerShell's formatting system stringifies a dictionary stored in a property of an input object, it treats it as an array of entries, and stringifies that array similarly to how arrays are stringified in expandable strings:

    • The difference is that the whole representation is enclosed in {...} and that the (stringified) elements are separated with , . You saw this in the display formatting of @{ 'a' = 1; 'b' = 2 }.psobject (.ImmediateBaseObject and .BaseObject properties).

    • I'm not aware of a public method that would produce this format, but it isn't hard to produce it yourself:

# -> '{[a, 1], [b, 2]}'
'{' + (@( @{ 'a' = 1; 'b' = 2 }.GetEnumerator() ) -join ', ') + '}'

Note:

  • The .GetEnumerator() call must be wrapped in @(...) to force actual enumeration, resulting in an array.

  • It follows that you can use the above for arrays as inputs too.


Getting an analogous string representation or [pscustomobject] instances is much simpler: Use string interpolation, i.e. embed the object in an expandable (double-quoted) string ("..."):

$obj = [pscustomobject] @{ 'a' = 1; 'b' = 2 }
"$obj" # -> '@{a=1; b=2}'

Note:

  • Unlike hashtables/dictionaries, [pscsutomobject] instances stringify the same way in expandable strings and as properties of formatted object representations.

  • Note the difference in formats compared to the hashtable visualization, with the property name-value pairs separate with = (and no whitespace), multiple properties separated with ;, and @{ as the opening delimiter.
    Curiously, this format is very similar to the syntax of a hashtable literal.


Finally, just to demonstrate that PowerShell's formatting system indeed produces the representations above for hashtables and [pscustomobject] instances used as properties:

@{ 
  hashtable = @{ 'a' = 1; 'b' = 2 }
  pscustomobject = [pscustomobject] @{ 'a' = 1; 'b' = 2 }
}

Output:

Name                           Value
----                           -----
pscustomobject                 @{a=1; b=2}
hashtable                      {[a, 1], [b, 2]}
0
Walter Mitty On

Try this:

$object = @{'a'=1; 'b'=2}
$x = [pscustomobject]$object

'Members'
$x | gm
'  '
'Content'
$x
'  '
'Properties'
$x.a
$x.b
'   '
'CSV format'
$x | ConvertTo-csv
'   '
'Json format'
$x | ConvertTo-Json

This will convert a and b into Noteproperty names, with the corresponding values.