I have a PSCustomObject that was created by converting a JSON array via ... | convertfrom-json
. The object has a lot of other objects for the property values (basically it's a collection of a lot of PSCustomObjects).
From knowing the object I know it contains at least three different types of objects (types meaning PSCustomObject with different properties).
When running Get-Member I only two object types and their members, the third one is not listed at all. I know there is a third object type as I can select properties that are only available in that object.
I did have once a similar issue, where some members would only appear in the results of get-member
only if called first in a $object | select...
method, otherwise they just didn't show up. I didn't figure it out then either. The current issue is not the same but might be related, as I tried the method of $object | select...
and it didn't help.
I did notice when trying to post code that is reproducible I get only one object type in return instead of two I get from the invoke-restmethod
, this makes my question even bigger, what's going on here, why are some object types returned and some not.
Example of get-member result
$res.address_objects.ipv4 | gm
TypeName: Selected.System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
host NoteProperty System.Management.Automation.PSCustomObject <snip>
name NoteProperty string name=<snip>
uuid NoteProperty string uuid=<snip>
zone NoteProperty string zone=<snip>
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
name NoteProperty string name=<snip>
network NoteProperty System.Management.Automation.PSCustomObject <snip>
uuid NoteProperty string uuid=<snip>
As you can see there are two object types here and they both have some different property names.
Taken from @Jawad's answer.
Please note: This sample is not an exact copy of my code as my psobject is the result of a invoke-restmethod
that automatically converts the json to an object.
$json = @"
{
"address_objects": {
"ipv4": [{
"host": "hostValue",
"name": "hostName",
"uuid": "value",
"zone": "thisZone"
},
{
"name": "NewName",
"network": "newNetwork",
"uuid": "thisUuid"
},
{
"name": "NewName",
"range": "newrange",
"uuid": "thisUuid"
}]
}
}
"@ | ConvertFrom-Json
Get-member
:$json.address_objects.ipv4 | gm
TypeName: Selected.System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
host NoteProperty System.Management.Automation.PSCustomObject <snip>
name NoteProperty string name=<snip>
uuid NoteProperty string uuid=<snip>
zone NoteProperty string zone=<snip>
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
name NoteProperty string name=<snip>
network NoteProperty System.Management.Automation.PSCustomObject <snip>
uuid NoteProperty string uuid=<snip>
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
name NoteProperty string name=<snip>
range NoteProperty System.Management.Automation.PSCustomObject <snip>
uuid NoteProperty string uuid=<snip>
Basically there is three distinct psCustomObjects so get-member
should list them all three.
Edited thanks to the commenter, they were right so I added a reproducible sample and clarified what I'm asking about. I haven't yet dissected in-depth the answers given.
Get-Member
by design lists the distinct types among its input objects.[1]
However, the problem with [pscustomobject]
instances is that Get-Member
does not recognize them as different types even if they have differing properties.
# Send 3 [pscustomobject] instances with distinct properties to Get-Member
[pscustomobject] @{ one = 1; two = 2; three = 3 },
[pscustomobject] @{ four = 4; five = 5 },
[pscustomobject] @{ six = 6; seven = 7 } | Get-Member
The following unexpectedly yields only a single output object, showing only the first [pscustomobject]
instance's members:
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
one NoteProperty int one=1
three NoteProperty int three=3
two NoteProperty int two=2
Get-Member
distinguishes types only by their (full) type names, as reflected in the hidden instance property .pstypenames
's first element (.pstypenames[0]
), without considering a given instance's specific properties.
That type name for [pscustomobject]
instances is System.Management.Automation.PSCustomObject
by default.
Note that .pstypenames[0]
by default contains the same type name as .GetType().FullName
, but "made-up" names may be inserted[2], which is what happens with [pscustomobject]
instances created by the Select-Object
cmdlet, for instance (see bottom section).
Workaround:
Note: The following works for display output (which should be fine, given that Get-Member
output is usually used for visual inspection).
[pscustomobject] @{ one = 1; two = 2; three = 3 },
[pscustomobject] @{ four = 4; five = 5 },
[pscustomobject] @{ six = 6; seven = 7 } |
Group-Object { "$($_.psobject.Properties.Name)" } | ForEach-Object {
Get-Member -InputObject $_.Group[0] | Out-Host
}
Group-Object
is used to group the input objects by their list of property names, using a calculated property (via a script block ({ ... }
) that is evaluated for each input object).
$_.psobject.Properties.Name
yields an array of all property names, and "$(...)"
converts that into a space-separated list.Each group is then processed via ForEach-Object
, passing each group's first instance ($_.Group[0]
) directly to Get-Member
Get-Member
calls produce individual display output, Out-Host
is used; without it, the display output would mistakenly suggest a single input type comprising the properties across all distinct types.If you're only interested in the list of distinct property names, across all input objects:
# This yields the sorted array of all unique property names, across all
# input objects:
# 'five', 'four', 'one', 'seven', 'six', 'three', 'two'
[pscustomobject] @{ one = 1; two = 2; three = 3 },
[pscustomobject] @{ four = 4; five = 5 },
[pscustomobject] @{ six = 6; seven = 7 } |
ForEach-Object { $_.psobject.Properties.Name } | Sort-Object -Unique
As for your symptoms:
Note that your first Get-Member
output block mentions a different type name: Selected.System.Management.Automation.PSCustomObject
The Selected.
prefix implies that the object was created via the Select-Object
cmdlet.
While such an object is technically also a [pscustomobject]
instance, the modified type name causes Get-Member
to treat it as a different type.
Here's a simplified example:
$obj = [pscustomobject] @{ one = 1; two = 2 }
$obj, ($obj | Select-Object -Property *) | Get-Member
This yields the following; note how the properties are the same and only the type name differs:
TypeName: System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
one NoteProperty int one=1
two NoteProperty int two=2
TypeName: Selected.System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
one NoteProperty int one=1
two NoteProperty int two=2
However, note that just like all [pscustomobject]
instances with type name System.Management.Automation.PSCustomObject
are treated the same even with differing properties, so are all the ones with Selected.System.Management.Automation.PSCustomObject
.
That is, Select-Object
-created [pscustomobject]
instances are also all treated the same, due to sharing the same, fixed type name.
[1] For instance, 1, 2, 3 | Get-Member
lists only one type, System.Int32
, because all input objects have that type; by contrast, 1, 'foo', 2 | Get-Member
lists two types, System.Int32
and System.String
(but not System.Int32
again).
[2] The ability to assign arbitrary type names is part of PowerShell's ETS (Extended Type System) - see about_types.ps1xml