Voo
Voo

Reputation: 30235

Different Evaluation for ConvertFrom-Json

Why do the two following snippets produce different output?

Get-Content -Raw "test.json" | ConvertFrom-Json | %{
    Write-Output "MessageType: $($_.Messagetype)"
}
# this time make sure Get-Content and ConvertFrom-Json are evaluated completely, before the foreach
(Get-Content -Raw "test.json" | ConvertFrom-Json) | %{
    Write-Output "MessageType: $($_.Messagetype)"
}

Executing the snippets with the following json:

[
  {
    "MessageType": "A"
  },
  {
    "MessageType": "B"
  }
]

the first script produces

MessageType: A B

and the second the expected

MessageType: A
MessageType: B

So basically for the first snippet, the foreach gets a single element which is an array with 2 elements, while in the second snippet the foreach is called for every element.

I don't understand why forcing the evaluation completely changes the behavior here.

Upvotes: 2

Views: 118

Answers (1)

mklement0
mklement0

Reputation: 440132

In PowerShell versions up to v6.x, ConvertFrom-Json, unlike most other cmdlets, outputs arrays as single objects, instead of sending objects one by one through the pipeline.

Therefore, in your first command's % (ForEach-Object) script block, $_ is the entire array, which, inside an expandable string ("...") stringifies to a list of its elements separated by spaces by default.

By contrast, enclosing a command in (), the grouping operator, turns it into an expression, and using an expression in a pipeline implicitly causes enumeration of the expression's result, causing the objects to be sent one by one.

Note: Another notable side effect of using () is that an enclosed command's output is collected in full in memory.

Therefore, your second command's % (ForEach-Object) script block is invoked twice, with $_ bound to a single object each.

Simplified examples:

# Sample JSON that is an array comprising 2 objects.
$json = '[
  {
    "MessageType": "A"
  },
  {
    "MessageType": "B"
  }
]'

# PS v6.x-: -> 1, because ConvertFrom-Json sent the array as a whole
$json | ConvertFrom-Json | Measure-Object | % Count

# -> 2, because using (...) resulted in enumeration
($json | ConvertFrom-Json) | Measure-Object | % Count

Upvotes: 2

Related Questions