Powershell Add-Member - 但在 JSON 中没有“Value”和“Count”元素

Tae*_*ous 1 powershell json cmdlet

我成功地向我的 JSON 添加了一个成员,但我最终得到了不需要的元素。我要添加的是出现在结果 JSON 中的“Value”中的元素。

{
"Block1": value1,
"Block2": value2,
"Block3": []
}
Run Code Online (Sandbox Code Playgroud)

然后执行 Add-Member cmdlet。

$objectFromJson |
  Add-Member -NotePropertyName "Block3" -NotePropertyValue $newblock -Force
Run Code Online (Sandbox Code Playgroud)

我意识到我不必执行-Force部分,但在我的工作代码中,我的 JSON 字符串使用ConvertFrom-Json解析为一个对象,并且该部分对我的目的有效。

存储在数组中有 1 到 N 个元素$newblock,要序列化为数组值属性Block3

不幸的是,我最终得到以下结果:

{
"Block1": value1,
"Block2": value2,
"Block3": [ { "value": { <elements of $newblock> }, "Count": <n> } ]
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码片段中,<elements of $newblock>表示$newblock数组元素的 JSON 表示,以及数组中元素<n>的数量。

它是有效的 JSON,但不是我想要的。相反,我希望 的元素是数组的$newblock直接元素Block3,而没有带有valueCount属性的无关包装器对象:

{
"Block1": value1,
"Block2": value2,
"Block3": [ <elements of $newblock> ]
}
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 5

tl;博士

  • 您自己的解决方案避免了原始问题,并且可以说是更好的开始方法:通过原始 JSON(而不是稍后通过)将.Block3属性创建为数组,然后使用 将元素添加到该数组中。Add-Member+=

  • 但是,您可以通过简单地(但模糊地)传递
    -NotePropertyValue $newblock.psobject.BaseObject而不是 来解决原始问题
    -NotePropertyValue $newblock,这会删除导致问题[psobject]的数组周围的不可见包装$newblock。请继续阅读以获取解释。


最后重要的是:存储在属性中数组不能有一个不可见的wrapper.Block3[psobject],因为Windows PowerShell 中,这会导致数组序列化为 JSON 包装在一个带有和properties的额外对象中"Count""values"

额外的对象源于.Count为数组命名的过时 ETS(扩展类型系统)属性的存在,该属性对[psobject]-wrapped 数组生效-有关详细信息,请参阅此答案

该问题不再出现在 PowerShell [Core] v6+ 中,因为此 ETS 属性已在那里删除。

Add-Member 是错误的方法,因为它在幕后添加了 PSObject 或 PSCustomObject。

实际上,Add-Member它本身并没有这样做,因为-NotePropertyValue参数是[object]-typed,而不是[psobject]-typed。

存储在您的$newblock变量中的数组必须已经被[psobject]包装:
$newblock -is [psobject]可能$true为您指示,而常规数组则没有(例如,1, 2 -is [psobject]is $false

例如,从 cmdlet返回的数组,作为一个整体将有一个不可见的[psobject]包装器,特别是当您使用New-Objectcmdlet 时:
(New-Object string[] 2) -is [psobject]返回$true

有关添加此不可见额外包装器的所有场景,请参阅此 GitHub 问题[psobject],这也可能导致其他细微的行为差异,这些差异仍会影响 v7.0 中的 PowerShell [Core](但是,如上所述,特定问题已得到修复通过删除 ETS 属性)。


有两种一般的解决方法

  • 会话范围

    • 在调用 之前ConvertTo-Json,运行以下命令,删除过时的 ETS 属性,之后数组按预期序列化 - 无论是否[psobject]-wrapped:
      Remove-TypeData System.Array
  • 对于给定的数组变量

    • 使用.psobject.BaseObject访问数组的展开,基础.NET阵列; 在你的情况下:$newblock.psobject.BaseObject

例子:

会话范围的解决方法:

# 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
]
Run Code Online (Sandbox Code Playgroud)

给定数组变量的解决方法

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
]
Run Code Online (Sandbox Code Playgroud)

  • 好东西。感谢您的深入分析。我希望我们的努力最终能帮助到别人。干杯。 (2认同)