如何在JSON中强制一个元素的数组生成方括号

ckx4rj1h  于 2023-06-25  发布在  其他
关注(0)|答案(2)|浏览(125)

我尝试使用以下方法创建一个JSON数组:

$bodyObject = @(
    @{    
        'Username' = 'email0@email.com'        
    }
)

$body = $bodyObject | ConvertTo-Json

但是$body对象不包含方括号:

{
    "Username":  "email0@email.com"
}

如果我向数组中添加另一个元素,代码可以完美地工作:

$bodyObject = @(
    @{    
        'Username' = 'email0@email.com'        
    },
    @{    
        'Username' = 'email1@email.com'        
    }
)

$body = $bodyObject | ConvertTo-Json
<# Output:
[
    {
        "Username":  "email0@email.com"
    },
    {
        "Username":  "email1@email.com"
    }
]
#>

如何让一个元素数组生成包含方括号的JSON?

yb3bgrhw

yb3bgrhw1#

最简单的方法是按位置传递数组,而不是通过管道:

$body = ConvertTo-Json $bodyObject

在第一个示例中看不到数组的原因是管道枚举。

ohtdti5x

ohtdti5x2#

补充Santiago's helpful answer

  • PowerShell的流水线的枚举行为意味着一个接收命令基本上无法区分作为(a)* 单个输入对象 * 或(b)作为 * 单个元素数组提供的流水线输入之间的区别。
  • 也就是说,以下两个命令 both 通过管道发送单个[int]示例:
  • (42 | ForEach-Object GetType).Name -> Int32
  • (@(42) | ForEach-Object GetType).Name -> Int32
  • 相比之下,当输入 * 作为参数 * 传递时,目标命令 * 可以 * 做出这样的区分- * 如果被设计为这样做的话 * -而ConvertTo-Json * 可以 *。
  • 然而,cmdlet很少有这种区别-请参阅GitHub issue #4242以了解相关讨论。

作为通过参数传递输入的替代方案,PowerShell (Core) 7+引入了
-AsArray switch
,它要求即使是单个输入对象(最初可能是一个单元素数组)在其JSON表示中被视为 array

# PS v7+ only; ditto for @(42) as input.
42 | ConvertTo-Json -AsArray -Compress # -> '[42]'

正如iRon所指出的,您可以通过确保给定数组(即使它只包含 * 一个 * 元素)通过pipeline * 作为一个整体发送*来实现相同的结果,这也适用于 Windows PowerShell

  • 注意:虽然使用ConvertTo-Json,将数组作为 * 参数 * 传递给ConvertTo-Json要简单得多,如圣地亚哥的回答所示,但下面的技术可能对不**支持传递数组值参数或只支持管道输入的命令感兴趣。
# Works in Windows PowerShell too.
# The unary form of the "," operator ensures that the array
# is sent *as a whole* through the pipeline.
, @(42) | ConvertTo-Json -Compress # -> '[42]'

,的一元形式,数组构造函数(“逗号”)运算符构造了一个 * transient ,辅助 * 数组:

  • 它的唯一元素是输入数组。
  • 当管道 * 枚举 * 这个数组时,它的一个也是唯一的元素-感兴趣的数组-通过管道 * 作为一个整体 * 发送。

还有一个不那么晦涩但效率不高的替代方案,使用Write-Output-NoEnumerate交换机:

# Works in Windows PowerShell too.
# -NoEnumerate prevents enumeration of the input array 
# and sends it through the pipeline as a whole.
Write-Output -NoEnumerate @(42) | ConvertTo-Json -Compress # -> '[42]'

注意事项:

  • 虽然 result 与v7+ -AsArray交换机相同,但机制不同:
  • 使用辅助数组/非枚举技术,ConvertTo-Json真正接收一个 array 作为其唯一的输入对象。
  • 对于v7+ -AsArray开关,当它接收标量(非数组)作为唯一的输入对象时,它仍然将其视为数组。
  • 如果接收到 * 多个 * 输入对象,-AsArray是无操作的,因为即使没有这个开关,也必须输出JSON数组,因为ConvertTo-Json总是预先收集它的输入,然后为它输出 * 单个 * JSON文档。
    • 不要 * 将-AsArray与 * 参数 * 结合使用(与管道输入相反),因为这将导致 * 嵌套 * JSON数组,至少在撰写本文时是这样(PowerShell 7.3.4):
ConvertTo-Json -AsArray -Compress @(42) # !! -> '[[42]]'
PowerShell枚举行为背后的设计原理:

PowerShell是围绕*管道 * 构建的:数据管道,对象 * 流 一次一个对象***。[1]
PowerShell命令默认输出到管道,并且
任何命令都可以写入 * 任何数量的对象 ,包括没有对象-并且 * 数量事先不知道**,因为它可能会因参数和外部状态而异。

  • 例如,Get-ChildItem *.txt可以不发射、发射1个或发射多个对象。

由于管道只是一个未指定计数的对象流,所以管道本身没有 * 数组 * 的概念,无论是输入还是输出

  • input 上,枚举数组(和大多数枚举)[2],即元素被一个接一个地发送到流水线。因此,如上所述,发送标量(单个对象)和通过管道发送单个元素数组之间没有区别。
  • output 上,多个对象只是一次输出一个(虽然可以,但很少,发送一个数组(或其他类似列表的类型)作为一个整体,但它本身只是管道中的另一个,单个输出对象)。
  • 只有当你收集一个管道的输出时,数组才会发挥作用,这是必然的:
  • 单个输出对象不需要容器,可以作为其自身接收。
  • 多个对象需要一个容器,PowerShell会自动创建一个System.Object[]数组来收集输出对象。

[1]您可以使用公共的-OutBuffer参数引入多个对象的 buffering,但管道中的下一个命令仍然逐个接收缓冲对象。
[2]有关详细信息,请参阅this answer的底部部分。

相关问题