问题
I am successfully adding a member to my JSON, but I end up with unwanted elements. What I am trying to add is the element inside "Value" that is appearing in the resulting JSON.
{
"Block1": value1,
"Block2": value2,
"Block3": []
}
Then doing the Add-Member cmdlet.
$objectFromJson |
Add-Member -NotePropertyName "Block3" -NotePropertyValue $newblock -Force
I realize I do not have to do the -Force part, but in my working code, my JSON string is parsed to an object using ConvertFrom-Json and that part is effective for my purposes.
There are from 1 to N elements in the array stored in $newblock
, to be serialized as array-valued property Block3
.
Unfortunately, I end up with the following:
{
"Block1": value1,
"Block2": value2,
"Block3": [ { "value": { <elements of $newblock> }, "Count": <n> } ]
}
In the snippet above, <elements of $newblock>
represent the JSON representation of the $newblock
array elements, and <n>
the count of elements in the array.
It is valid JSON, but not what I desire. Instead, I want the elements of $newblock
to be direct elements of the Block3
array, without the extraneous wrapper object with the value
and Count
properties:
{
"Block1": value1,
"Block2": value2,
"Block3": [ <elements of $newblock> ]
}
回答1:
The solution to my issue was as follows (pseudo code):
$json = @"
{
"Block1": value1,
"Block2": value2,
"Block3": []
}
"@
$objFromJson = $json | ConvertFrom-Json
$listCount = ($newblock.ToCharArray() | Where-Object ($_ -eq ";" | Measure-Object).Count + 1
for($i = 0; $i -lt $listCount; $i++) {
$newJson = @{ element1 = value1; element2 = value2; etc. }
$objFromJson.Block3 += $newJson
}
$objFromJson | ConvertTo-Json
The part I had issues with was that Add-Member
was the wrong approach, since it adds a PSObject or PSCustomObject under the covers.
Thanks to @mklement0 for pointing me to the root cause of my question.
回答2:
tl;dr
Your own solution avoids the original problem, and is arguably the better approach to begin with: Create the
.Block3
property as an array via the original JSON (rather than later viaAdd-Member
), and add elements to that array later with+=
.However, you could have fixed the original problem by simply (but obscurely) passing
-NotePropertyValue $newblock.psobject.BaseObject
instead of-NotePropertyValue $newblock
, which removes the invisible[psobject]
wrapper around the array stored in$newblock
that caused the problem. Read on for an explanation.
What matters in the end is: the array stored in the .Block3
property must not have an invisible [psobject]
wrapper, because in Windows PowerShell that causes the array to serialize to JSON wrapped in an extra object with "Count"
and "values"
properties.
The extra object stem from the presence of an obsolete ETS (extended-type system) property named .Count
for arrays, which takes effect for [psobject]
-wrapped arrays - see this answer for the gory details.
The problem no longer surfaces in PowerShell [Core] v6+, because this ETS property has been removed there.
Add-Member was the wrong approach, since it adds a PSObject or PSCustomObject under the covers.
Actually, Add-Member
by itself does not do that, because the -NotePropertyValue
parameter is [object]
-typed, not [psobject]
-typed.
The array stored in your $newblock
variable must already have been [psobject]
wrapped: $newblock -is [psobject]
probably indicates $true
for you, whereas a regular array does not (e.g., 1, 2 -is [psobject]
is $false
)
For instance, an array returned from a cmdlet, as a whole will have an invisible [psobject]
wrapper, notably when you use the New-Object
cmdlet: (New-Object string[] 2) -is [psobject]
returns $true
See this GitHub issue for all scenarios in which this invisible extra [psobject]
wrapper is added, which can cause other subtle behavioral differences as well, which still affect PowerShell [Core] as of v7.0 (but, as stated, this particular issue has been fixed by the removal of the ETS property).
There are two general workarounds:
Session-wide:
- Before calling
ConvertTo-Json
, run the following command, which removes the obsolete ETS property, after which arrays serialize as expected - whether[psobject]
-wrapped or not:Remove-TypeData System.Array
- Before calling
For a given array variable:
- Use
.psobject.BaseObject
to access the array's unwrapped, underlying .NET array; in your case:$newblock.psobject.BaseObject
- Use
Examples:
Session-wide workaround:
# The problem: Serialize a [psobject]-wrapped array (0, 0):
PS> ConvertTo-Json -InputObject (New-Object int[] 2)
# Note the extra object with the "count" (element count) and "value" property (elements)
{
"value": [
0,
0
],
"Count": 2
}
# Remove the ETS definitions for System.Array
Remove-TypeData System.Array
# Rerun the command:
PS> ConvertTo-Json -InputObject (New-Object int[] 2)
# OK
[
0,
0
]
Workaround for a given array variable:
PS> $arr = New-Object int[] 2; ConvertTo-Json -InputObject $arr
# Note the extra object with the "count" (element count) and "value" property (elements)
{
"value": [
0,
0
],
"Count": 2
}
# $arr.psobject.BaseObject bypasses the [psobject] wrapper
PS> ConvertTo-Json -InputObject $arr.psobject.BaseObject
# OK
[
0,
0
]
来源:https://stackoverflow.com/questions/57576341/powershell-add-member-but-without-value-and-count-elements-in-json