What operator should be used to detect an empty psobject?

It is the simplest solution to test for an empty (property-less) custom object ([pscustomobject]) via its string representation, but you need to use an expandable string (string interpolation, "...") rather than .ToString() to obtain it:

# Returns $True, if custom object $test is empty, i.e. has no properties
-not "$test"

Note: -not $test.ToString() should be equivalent, but currently (as of PowerShell Core 6.1) isn't, due to a bug. With the bug present, any [pscustomobject] instance returns the empty string from.ToString().
Another workaround is to use .psobject.ToString().

Only an empty (property-less) custom object stringifies to the empty string inside an expandable string, and coercing an empty string to a Boolean in PowerShell yields $False, whereas any nonempty string yields $True.

The alternative is to compare against an empty string as the LHS, which implicitly forces the [pscustomobject] on the RHS to be stringified:

# NOTE: Works ONLY with '' on the LHS.
'' -eq $test

A conceptually clearer approach, though it relies on the hidden .psobject property PowerShell adds to all objects, containing reflection information:

0 -eq @($test.psobject.Properties).Count

Note the need to use @(...) to force enumeration of the properties so that they can be counted - see next section.

The above methods are convenient, but if $test is a large object with many properties, it can be expensive - though in absolute terms that will propbably rarely matter in practice.


A less expensive, but more obscure solution is to access the .psobject.Properties collection without enumerating all its members:

# Returns $true, if $test has no properties
-not $test.psobject.Properties.GetEnumerator().MoveNext()

The .psobject.Properties collection is apparently lazily enumerated and therefore doesn't have a .Count property; using .GetEnumerator().MoveNext() is therefore a way to limit enumeration to the first property, if any.


As for what you tried:

$test -eq $null

$test is still an object, even if it happens to have no properties, and an object is by definition never $null.

-not $test

PowerShell's implicit to-Boolean conversion treats any [pscustomobject] instance as $True, whether or not it happens to have properties; e.g., [bool] ([pscustomobject] @{}) yields $True.

To see how other data types are coerced to Booleans, see this answer.


Probably more expensive, but less obscure; is using the the native Get-Member cmdlet:

[Bool]($Test | Get-Member -MemberType NoteProperty)

Note that $Test should not be $Null (rather than an empty object) otherwise it will produce an error (as with using methods on $Null). To avoid this you might also consider using:

$Test -and ($Test | Get-Member -MemberType NoteProperty)

use string tests & test with the $Var on the right side of the comparison so that it is coerced to the type on the left. you can also test with the [string] methods below ... [grin]

$Test = '{ }' | ConvertFrom-Json

$Test -eq $Null
$Null -eq $Test
$Test -eq ''
''
'' -eq $Test
[string]::IsNullOrEmpty($Test)
[string]::IsNullOrWhiteSpace($Test)

output ...

False
False
False

True
True
True

Tags:

Powershell