If I run this in PowerShell, I expect to see the output 0 (zero):
Set-StrictMode -Version Latest
$x = "[]" | ConvertFrom-Json | Where { $_.name -eq "Baz" }
Write-Host $x.Count
Instead, I get this error:
The property 'name' cannot be found on this object. Verify that the property exists and can be set.
At line:1 char:44
+ $x = "[]" | ConvertFrom-Json | Where { $_.name -eq "Baz" }
+ ~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyAssignmentException
If I put braces around "[]" | ConvertFrom-Json it becomes this:
$y = ("[]" | ConvertFrom-Json) | Where { $_.name -eq "Baz" }
Write-Host $y.Count
And then it "works".
What is wrong before introducing the parentheses?
To explain the quotes around "works" - setting strict mode Set-StrictMode -Version Latest indicates that I call .Count on a $null object. That is solved by wrapping in @():
$z = @(("[]" | ConvertFrom-Json) | Where { $_.name -eq "Baz" })
Write-Host $z.Count
I find this quite dissatisfying, but it's an aside to the actual question.
Because
ConvertFrom-JsontellsWhere-Objectto not attempt to enumerate its output.Therefore, PowerShell attempts to access the
nameproperty on the empty array itself, much like if we were to do:When you enclose
ConvertFrom-Jsonin parentheses, powershell interprets it as a separate pipeline that executes and ends before any output can be sent toWhere-Object, andWhere-Objectcan therefore not know thatConvertFrom-Jsonwanted it to treat the array as such.We can recreate this behavior in powershell by explicitly calling
Write-Outputwith the-NoEnumerateswitch parameter set:Write-Output -NoEnumerateinternally callsCmdlet.WriteObject(arg, false), which in turn causes the runtime to not enumerate theargvalue during parameter binding against the downstream cmdlet (in your caseWhere-Object)In the specific context of parsing JSON, this behavior might indeed be desirable:
Should I not expect exactly 5 objects from
ConvertFrom-Jsonnow that I passed 5 valid JSON documents to it? :-)