I am calling an API that returns 3 values in an object "tags", which has values for "tags.name" and tags.results". The values below are what is returned. But as I try to navigate through the values, I can see that the "null" value, the {}, was not stored in the array. It just skipped it, but I need this value as it completely ruins the array.
Any idea how to correctly populate the array so it doesn't skip this {} value?
Response Values
values attributes
------ ----------
{1605560351000 78.129448 3} @{machine_type=System.Object[]}
{}
{1605560354000 0 3} @{machine_type=System.Object[]}
Resulting Array
0 : 1605560351000 78.129448 3
1 : 1605560354000 0 3
2 :
The PowerShell nvoke and array code:
$response = Invoke-RestMethod 'https://api' -Method 'POST' -Headers $headers -Body $body
"Response Values"
$response.tags.results
""
"Resulting Array"
"0 : " + $response.tags.results.values[0]
"1 : " + $response.tags.results.values[1]
"2 : " + $response.tags.results.values[2]
The returned JSON from Invoke-RestAPI. You can see where the returned value is null for the second node.
{
"tags": [
{
"name": "StateComp1",
"results": [
{
"values": [
[
1605561152000,
75.436455,
3
]
],
}
],
},
{
"name": "StateComp2",
"results": [
{
"values": [],
}
],
},
{
"name": "StateComp3",
"results": [
{
"values": [
[
1605561469000,
0,
3
]
],
}
],
}
]
}
The problem is not specific to
Invoke-RestMethod
and is instead explained by the behavior of PowerShell's member-access enumeration feature:When you access
$response.tags.results.values
, the.values
properties on the - multiple -.results
properties (you're using nested member-access enumeration) are effectively enumerated as follows:In doing so, empty arrays are effectively removed from the output, and you get only 2 output objects (that each contain the inner array of the nested ones in your case); you can verify that by applying
(...).Count
to the command above.The reason is that objects output by a script block (
{ ... }
) are sent to the pipeline, which by default enumerates objects that are collections (arrays).Since your 2nd
.values
value is an empty array (parsed from JSON[]
and therefore not$null
), there is nothing to enumerate and nothing is output, resulting in effective removal.Note that the above implies that collections of collections are flattened by member-access enumeration; for instance, if the property values are 3 2-element arrays, the pipeline-enumeration logic results in a single 6-element array rather than in a 3-element array containing 2-element arrays each.
The workaround is to to use the
ForEach()
array method, which doesn't perform this stripping if you target the property by supplying its name as a string:Note that your non-empty
.values
properties are actually nested arrays; to shed the outer array, use$values[0][0]
,$values[1][0]
, and$values[2][0]
.Caveat: The workaround is only effective if you access the property by name string -
.ForEach('values')
; the seemingly equivalent script-block based command,.ForEach({ $_.values })
again removes empty arrays, like member-access enumeration and theForEach-Object
cmdlet; alternatively, however, you can work around that by wrapping the script-block output in an auxiliary, temporary single-element array that preserves the original arrays, using the unary form of,
the array constructor operator:.ForEach({ , $_.values })
You could also use the same technique with the - slower -
ForEach-Object
cmdlet.