Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConvertTo-Json for empty array produces null #23837

Open
5 tasks done
smokedlinq opened this issue May 23, 2024 · 9 comments
Open
5 tasks done

ConvertTo-Json for empty array produces null #23837

smokedlinq opened this issue May 23, 2024 · 9 comments
Labels
Needs-Triage The issue is new and needs to be triaged by a work group.

Comments

@smokedlinq
Copy link

smokedlinq commented May 23, 2024

Prerequisites

Steps to reproduce

$null -eq (@() | ConvertTo-Json -AsArray) is $true but should be false because an empty array in json is []

Expected behavior

PS> @() | ConvertTo-Json -AsArray
[]

Actual behavior

PS> @() | ConvertTo-Json -AsArray

Error details

N/A

Environment data

Name                           Value
----                           -----
PSVersion                      7.4.2
PSEdition                      Core
GitCommitId                    7.4.2
OS                             Microsoft Windows 10.0.26120
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

@smokedlinq smokedlinq added the Needs-Triage The issue is new and needs to be triaged by a work group. label May 23, 2024
@smokedlinq
Copy link
Author

I do realize this is because the pipeline has no items to send to the cmdlet, so the begin and end happen and no output, I would expect that the -AsArray with no items would be an empty json array []

@smokedlinq
Copy link
Author

If others want a workaround, use ConvertTo-Json -InputObject @() -AsArray but replace the @() with a variable since most likely that's what you are doing.

@mklement0
Copy link
Contributor

mklement0 commented May 23, 2024

@() sending nothing to the pipeline is fundamental pipeline behavior (the auto-enumeration of an empty enumerable results in no enumeration); ConvertTo-Json simply receives no input, and therefore outputs $null (technically, [System.Management.Automation.Internal.AutomationNull]::Value, which in expressions behaves like $null).

Indeed, ConvertTo-Json -InputObject @() is one workaround; in the pipeline, use:

Write-Output -NoEnumerate @() | ConvertTo-Json

# More succinct, but obscure shortcut:
, @() | ConvertTo-Json

Note:

@roe-dk
Copy link

roe-dk commented May 24, 2024

$Array | ConvertTo-Json will also flatten a single-item array so, in my experience, you are always better off using the -InputObject parameter

PS>@("OneValue") | ConvertTo-Json
"OneValue"
PS>ConvertTo-Json -InputObject @("OneValue")
[
  "OneValue"
]
PS>

@mklement0
Copy link
Contributor

mklement0 commented May 24, 2024

Good point, @roe-dk; since ConvertTo-Json of necessity collects all its pipeline input up front anyway before producing its single-string output, there's effectively no loss of streaming functionality due to use of a direct argument (with -InputObject, which, btw, in ConvertTo-Json's case is a positional parameter, so that ConvertTo-Json @("OneValue") works too).

-AsArray is helpful if you have open-ended, streaming input from a cmdlet call, and you want to output a JSON array even if the cmdlet call, say Get-ChildItem *.txt, situationally only yields one output object:

# Ensures that a JSON *array* is created even if only ONE file matches.
# Two or more input objects of necessity always result in a JSON array.
Get-ChildItem *.txt | Select-Object Name | ConvertTo-Json -AsArray

However, as in the case at hand, there will be NO output if no files matching *.txt happen to be present (which, as noted, is signaled by the [System.Management.Automation.Internal.AutomationNull]::Value singleton as the output object, which presents as $null in expressions such as $null -eq [System.Management.Automation.Internal.AutomationNull]::Value) .

By contrast, if you use @(...), the array-subexpression operator, with -InputObject, the no-input-object case yields '[]'.

# -> '[]'
ConvertTo-Json @(Get-ChildItem nosuch* | Select-Object Name )

Using just (...), the grouping operator yields 'null' in the no-input-object case.

# -> 'null'
ConvertTo-Json (Get-ChildItem nosuch* | Select-Object Name )

The technical reason is that [System.Management.Automation.Internal.AutomationNull]::Value gets coerced to $null during parameter binding, so that the above is equal to ConvertTo-Json $null


Due to the behavior discussed in #10952, -AsArray mustn't be combined with input that already is an array (unless an extra array wrapper is desired), either due to passing an array directly to -InputObject or due to sending an array as a whole through the pipeline (Write-Output -NoEnumerate $array | ConvertTo-Json or , $array | ConvertTo-Json).

@smokedlinq
Copy link
Author

If we explicitly state we want it as an array using -AsArray, and the stream is empty, shouldn't we get an empty array?

@mklement0
Copy link
Contributor

The problem is that that there is no "it" in your scenario: ConvertTo-Json receives no input, and therefore produces no output, so -AsArray - which modifies the output - doesn't apply.

@mklement0
Copy link
Contributor

Then again, your point is defensible too: wrap the to-be-JSON-converted input in an array, and if there's no input, wrap "nothing" in an array, i.e. output an empty array.

On further reflection I personally agree that this behavior would be more helpful,
but it's technically a breaking change.

I guess we'll have to wait for what the relevant WG has to say.

@smokedlinq
Copy link
Author

I understand and knew it's be a breaking behavior going in. Hoping for a 7.5 or 7.6 change 😍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Needs-Triage The issue is new and needs to be triaged by a work group.
Projects
None yet
Development

No branches or pull requests

3 participants