I am trying to recurse parsed JSON, but value types are passing type checks for [psobject] and [pscustomobject] making the recursive case impossible to detect.
function repro($v) {
$v.GetType()
$v -is [psobject]
}
repro ([int64]4)
repro ("4" | ConvertFrom-Json)
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int64 System.ValueType
False
True True Int64 System.ValueType
False
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Int64 System.ValueType
False
True True Int64 System.ValueType
True
Name Value
---- -----
PSVersion 6.2.0
PSEdition Core
GitCommitId 6.2.0
OS Darwin 18.5.0 Darwin Kernel Version 18.5.0: Mon Mar 11 20:40:32 PDT 2019; root:xnu-4903.251.3~3/RELEASE_X86_64
Platform Unix
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0鈥
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
Also observed on Windows
Yeah. _Every_ type you can find in PowerShell is wrapped in a [psobject], by design. PSObject is a wrapper type that enables the Extended Type System functions used pretty extensively in PS.
For that matter, -is [PSObject] and -is [PSCustomObject] are identical; if you check either type's fullname you'll see they point to the same class. The difference there is how the parser handles the [pscustomobject] version differently, specifically in the [pscustomobject]@{} situation where it's followed by a hashtable.
If you need to find a PSCustomObject type specifically, you'll need to do your checks differently.
function Test-PSCustomObject {
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromPipeline)]
[psobject]
$InputObject
)
process {
$InputObject.PSTypeNames -contains 'System.Management.Automation.PSCustomObject'
}
}
Test-PSCustomObject ([int64]4) # gives False
Test-PSCustomObject ("4" | ConvertFrom-Json) # gives False
Test-PSCustomObject ('{"property":"value"}'| ConvertFrom-Json) # gives True
For my use case, I ended up using -isnot [ValueType]. It's pretty common for interpreted languages to have wrapper types and I'm OK with that. In that case, I would expect ([int64]4) -is [psobject] to be $true. My bug is that the wrapper type is inconsistently applied (as evident with -is [psobject]) and not clearly exposed to the user (.GetType() outputs are identical)
Can you check again with 6.2? I checked that case briefly and I'm pretty sure it was giving back what you expect there. I'll check again, just to be sure.
Also, I don't think [string] will register as a value type, from memory, so mind that. :)
I just updated to 6.2 and I'm still observing the same output. Also confirmed you are right about -isnot [ValueType]
Hmm, you are correct. @SteveL-MSFT is there a provision for basic value types where they aren't usually wrapped in PSObject wrappers for some reason?
@chriskuech I'd probably recommend using a switch in this instance for processing the items in your json, something along the lines of:
switch ($Item) {
{$_ -is [string]} {
# process $_ as string
}
{$_ -is [ValueType]} {
# process $_ as value type
}
default {
# process $_ as object / custom object (JSON only generates custom objects anyway)
}
}
Thanks, I did not know scriptblocks are acceptable switch cases. In this case I have to use ifs because it's a merge function on two objects, not just a traverse function on a single object.
Yeah, switch does some fun things. More on that from Kevin Marquette's lovely comprehensive blog post here: https://powershellexplained.com/2018-01-12-Powershell-switch-statement/
If you need more help with this, feel free to jump into the PS Discord / Slack channels as well. 馃槃
@vexx32 unfortunately, I'm not familiar with the history of this
My bug is that the wrapper type is inconsistently applied
Indeed - see #5579 and #4343.
However, -is [System.Management.Automation.PSCustomObject] works consistently:
([int64] 4),
("4" | ConvertFrom-Json),
([pscustomobject] @{ val = 4 }),
([pscustomobject] @{ val = 4 } | Select-Object val) |
& {
param([Parameter(ValueFromPipeline)] $obj)
process {
$obj -is [System.Management.Automation.PSCustomObject]
}
}
-> $false, $false, $true, $true