从powershell 3提示符,我想调用一个RESTful服务,获得一些JSON,并打印它.我发现如果我将数据转换为powershell对象,然后将powershell对象转换回json,我会得到一个漂亮的漂亮打印字符串.但是,如果我将两个转换组合成一个带管道的单线程,我会得到不同的结果.
TL; DR:这个:
PS> $psobj = $orig | ConvertFrom-JSON
PS> $psobj | ConvertTo-JSON
Run Code Online (Sandbox Code Playgroud)
......给我的结果与此不同:
PS> $orig | ConvertFrom-JSON | ConvertTo-JSON
Run Code Online (Sandbox Code Playgroud)
[
{
"Type": "1",
"Name": "QA"
},
{
"Type": "2",
"Name": "whatver"
}
]
Run Code Online (Sandbox Code Playgroud)
我将删除空格(因此它适合一行...),将其转换为powershell对象,然后将其转换回JSON.这很好用,并返回正确的数据:
PS> $orig = '[{"Type": "1","Name": "QA"},{"Type": "2","Name": "DEV"}]'
PS> $psobj = $orig | ConvertFrom-JSON
PS> $psobj | ConvertTo-JSON
[
{
"Type": "1",
"Name": "QA"
},
{
"Type": "2",
"Name": "DEV"
}
]
Run Code Online (Sandbox Code Playgroud)
但是,如果我将最后两个语句组合成一个单行,我会得到不同的结果:
PS> $orig | ConvertFrom-JSON | ConvertTo-JSON
{
"value": [
{
"Type": "1",
"Name": "QA"
},
{
"Type": "2",
"Name": "DEV"
}
],
"Count": 2
}
Run Code Online (Sandbox Code Playgroud)
注意添加了"value"和"Count"键.为什么会有区别?我确定它与返回JSON对象而不是JSON数组的愿望有关,但我不明白为什么我进行转换的方式会影响最终结果.
注意:自Windows PowerShell v5.1起,问题仍然存在,但PowerShell Core不受影响.
现有的答案提供了有效的解决方法 -封闭$orig | ConvertFrom-JSON的(...)-但不要解释正确的问题; 此外,解决方法不能在所有情况下使用.
至于为什么使用中间变量没有出现问题:
如果在变量中收集输出,那么逐个发射数组元素与作为整体的数组(作为单个对象)之间的流水线区别是无效的; 例如,有效地等同于,即使后者最初将数组作为单个对象发出; 但是,如果进一步的管道段处理对象,则区别很重要 - 见下文.$a = 1, 2$a = Write-Output -NoEnumerate 1, 21, 2
该问题的行为是组合两个因素:
ConvertFrom-Json通过管道将数组作为单个对象发送,从而偏离正常的输出行为.也就是说,使用表示数组的JSON字符串,ConvertFrom-Json将生成的对象数组作为单个对象通过管道发送.
您可以验证ConvertFrom-Json以下令人惊讶的行为:
PS> '[ "one", "two" ]' | ConvertFrom-Json | Get-Member
TypeName: System.Object[] # !! should be: System.String
...
Run Code Online (Sandbox Code Playgroud)
如果ConvertFrom-Json通过管道逐个传递其输出 - 正如cmdlet通常所做的那样 - Get-Member将返回集合中的(不同的)类型的项目,[System.String]在这种情况下.
将命令包含在(...)强制枚举其输出中,这就是为什么($orig | ConvertFrom-Json) | ConvertTo-Json有效的解决方法.
这个GitHub问题正在讨论这种行为 - 它仍然存在于PowerShell Core中 - 应该改变.
该System.Array类型-基本类型为所有阵列-具有.Count经由Powershell的ETS(扩展类型系统-见为它定义属性Get-Help about_Types.ps1xml),这会导致ConvertTo-Json包括在JSON字符串该属性它创建,与包括在同级数组元素value属性.
只有在ConvertTo-Json将数组作为整体看作输入对象时才会发生这种情况,就像ConvertFrom-Json在这种情况下产生的那样; 例如,, (1, 2) | ConvertTo-Json表示问题(一个嵌套数组,其内部数组作为单个对象发送),但
1, 2 | ConvertTo-Json没有(数组元素是单独发送的).
这个ETS提供的.Count属性在PSv3中被有效地淘汰了,当数组隐含地获得了一个.Count属性时,由于PowerShell现在也出现了明确实现的接口成员,它们表现了ICollection.Count属性(另外,为了统一处理,所有对象都被赋予了一个隐含 .Count属性)标量和集合).
因此,在PowerShell Core中已删除此ETS属性,但仍存在于Windows PowerShell v5.1中 - 请参阅下面的解决方法.
与PetSerAl一样多次出现过帽子的提示.
注意:根据定义,此解决方法是PSv3 +,因为Convert*-Jsoncmdlet仅在v3中引入.
鉴于 ETS提供的.Count属性是(a)问题的原因和(b)在PSv3 +中有效地过时,解决方案是在调用之前简单地删除它- 在会话中执行此操作就足够了,并且它不应该影响其他命令:ConvertTo-Json
Remove-TypeData System.Array # Remove the redundant ETS-supplied .Count property
Run Code Online (Sandbox Code Playgroud)
有了这个,外来的.Count和.value属性应该消失了:
PS> '[ "one", "two" ]' | ConvertFrom-Json | ConvertTo-Json
[
"one",
"two"
]
Run Code Online (Sandbox Code Playgroud)
上述解决方法还修复了数组值属性的问题 ; 例如:
PS> '' | Select-Object @{ n='prop'; e={ @( 1, 2 ) } } | ConvertTo-Json
{
"prop": [
1,
2
]
}
Run Code Online (Sandbox Code Playgroud)
如果没有解决方法,其值"prop"将包括无关.Count和.value属性.
解决方案是用括号包装前两个操作:
PS C:\> ($orig | ConvertFrom-JSON) | ConvertTo-JSON
[
{
"Type": "1",
"Name": "QA"
},
{
"Type": "2",
"Name": "DEV"
}
]
Run Code Online (Sandbox Code Playgroud)
括号允许您一次抓取前两个操作的输出.如果没有它们,powershell将尝试解析它单独获取的任何对象.收集PSCustomObject从所得$orig | ConvertFrom-JSON包含两个PSCustomObjects用于1/QA和2/DEV对,所以通过管道该集合的powershell的输出试图处理该键/值对的一在-A-时间.
使用括号是对输出进行"分组"的一种较短方式,允许您在不创建变量的情况下对其进行操作.
| 归档时间: |
|
| 查看次数: |
3222 次 |
| 最近记录: |