为什么在转换JSON时,powershell在一行程序中给出的结果与在两行程序中给出的结果不同? 概述
在PowerShell3提示符下,我想调用一个RESTful服务,获取一些JSON,并将其打印出来。我发现,如果我将数据转换为powershell对象,然后将powershell对象转换回json,我会得到一个漂亮的打印字符串。但是,如果我将这两种转换组合成一个带有管道的内衬,我将得到不同的结果 TL;DR:这是:为什么在转换JSON时,powershell在一行程序中给出的结果与在两行程序中给出的结果不同? 概述,json,powershell,Json,Powershell,在PowerShell3提示符下,我想调用一个RESTful服务,获取一些JSON,并将其打印出来。我发现,如果我将数据转换为powershell对象,然后将powershell对象转换回json,我会得到一个漂亮的打印字符串。但是,如果我将这两种转换组合成一个带有管道的内衬,我将得到不同的结果 TL;DR:这是: PS> $psobj = $orig | ConvertFrom-JSON PS> $psobj | ConvertTo-JSON 。。。给出了与此不同的结果: PS&
PS> $psobj = $orig | ConvertFrom-JSON
PS> $psobj | ConvertTo-JSON
。。。给出了与此不同的结果:
PS> $orig | ConvertFrom-JSON | ConvertTo-JSON
原始数据
分两步进行转换
我将删除空白(因此它适合一行…),将其转换为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"
}
]
将这两个步骤与管道相结合
但是,如果我将最后两条语句组合成一行,我会得到不同的结果:
PS> $orig | ConvertFrom-JSON | ConvertTo-JSON
{
"value": [
{
"Type": "1",
"Name": "QA"
},
{
"Type": "2",
"Name": "DEV"
}
],
"Count": 2
}
注意添加了键“value”和“Count”。为什么会有区别?我确信这与返回JSON对象而不是JSON数组的愿望有关,但我不明白为什么转换的方式会影响最终结果 首先,为什么会发生这种情况 PowerShell会自动将多个对象包装到名为
PSMemberSet
的集合中,该集合上有Count
属性。这基本上就是PowerShell管理任意对象数组的方式。发生的情况是,Count
属性被添加到生成的JSON中,产生了您所看到的不希望的结果
我们可以通过以下方式证明我刚才所说的:
$Json = @"
[
{
"Type": "1",
"Name": "QA"
},
{
"Type": "2",
"Name": "DEV"
}
]
"@;
# Deserialize the JSON into an array of "PSCustomObject" objects
$Deserialized = ConvertFrom-Json -InputObject $Json;
# Examine the PSBase property of the PowerShell array
# Note the .NET object type name: System.Management.Automation.PSMemberSet
$Deserialized.psbase | Get-Member;
[
{
"Type": "1",
"Name": "QA"
},
{
"Type": "2",
"Name": "DEV"
}
]
[
{
"Type": "1",
"Name": "QA"
},
{
"Type": "2",
"Name": "DEV"
}
]
这是上面的输出
TypeName: System.Management.Automation.PSMemberSet
Name MemberType Definition
---- ---------- ----------
Add Method int IList.Add(System.Object value)
Address Method System.Object&, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Address(int )
Clear Method void IList.Clear()
......
......
Count Property int Count {get;}
您可以通过引用PSMemberSet
(实现ICollection
.NET接口)的SyncRoot
属性,并将该属性的值传递给converttojson
,来解决此行为
下面是一个完整的工作示例:
$Json = @"
[
{
"Type": "1",
"Name": "QA"
},
{
"Type": "2",
"Name": "DEV"
}
]
"@;
($Json | ConvertFrom-Json) | ConvertTo-Json;
将显示正确(预期)的输出,如下所示:
$Json = @"
[
{
"Type": "1",
"Name": "QA"
},
{
"Type": "2",
"Name": "DEV"
}
]
"@;
# Deserialize the JSON into an array of "PSCustomObject" objects
$Deserialized = ConvertFrom-Json -InputObject $Json;
# Examine the PSBase property of the PowerShell array
# Note the .NET object type name: System.Management.Automation.PSMemberSet
$Deserialized.psbase | Get-Member;
[
{
"Type": "1",
"Name": "QA"
},
{
"Type": "2",
"Name": "DEV"
}
]
[
{
"Type": "1",
"Name": "QA"
},
{
"Type": "2",
"Name": "DEV"
}
]
解决方案是将前两个操作用括号括起来:
PS C:\> ($orig | ConvertFrom-JSON) | ConvertTo-JSON
[
{
"Type": "1",
"Name": "QA"
},
{
"Type": "2",
"Name": "DEV"
}
]
括号允许您同时获取前两个操作的输出。没有它们,powershell将尝试单独解析其获取的任何对象。由$orig | ConvertFrom JSON
生成的PSCustomObject
集合包含两个PSCustomObjects
用于1/QA和2/DEV对,因此,通过管道传输该集合的输出,powershell尝试一次处理一个键/值对
使用括号是对输出进行“分组”的一种较短方式,允许您在不生成变量的情况下对其进行操作。注意:自Windows PowerShell v5.1起,问题仍然存在,但PowerShell Core(v6+)不受影响。 现有的答案提供了一个有效的解决方法-在
(…)
中包含$orig | convertfromJSON
,但没有正确解释问题;此外,解决方案不能用于所有情况
至于为什么使用中间变量没有出现问题: 如果在变量中收集输出,则逐个发射数组元素与作为整体(作为单个对象)的数组之间的管道内区别将为空;e、 例如,
$a=1,2
实际上等同于$a=Write Output-NoEnumerate 1,2
,即使后者最初作为单个对象发射数组1,2
;但是,如果进一步的管道段处理对象,则区别很重要-请参见下文
问题行为是两个因素的组合:
通过管道将数组作为单个对象发送,从而偏离了正常的输出行为。也就是说,使用表示数组的JSON字符串,convertfromjson
通过管道将生成的对象数组作为单个对象发送ConvertFrom JSON
- 您可以验证
的惊人行为,如下所示:convertfromjson
PS> '[ "one", "two" ]' | ConvertFrom-Json | Get-Member TypeName: System.Object[] # !! should be: System.String ...
- 如果
像cmdlet通常做的那样通过管道逐个传递其输出,ConvertFrom-Json
将返回集合中项目的(不同)类型,在本例中为Get-Member
[System.String]
- 在
中包含命令将强制枚举其输出,这就是为什么(…)
是一种有效的解决方法($orig | ConvertFrom Json)| ConvertTo Json
- 在
- 这一行为是否应该改变(也仍然存在于PowerShell核心中)正在讨论中
- 您可以验证
类型-所有数组的基本类型-通过PowerShell的ETS(扩展类型系统-请参阅)为其定义了一个系统.Array
属性,该属性导致.Count
将该属性包含在其创建的Json字符串中,包含在同级转换为Json
值
属性中的数组元素
- 只有当
将数组作为一个整体convertToJSON
- 只有当
ConvertFrom Json
生成的;e、 例如,,(1,2)|converttojson
解决了这个问题(一个嵌套数组,其内部数组作为单个对象发送),但是1,2 | converttojson
没有(数组元素单独发送)
.Count
属性在PSv3中被有效地废弃,因为PowerShell现在也显式地呈现了实现接口成员,从而使数组隐式地获得了.Count
属性,从而呈现了ICollection.Count
属性(此外,所有对象都在
PS> '[ "one", "two" ]' | ConvertFrom-Json | ConvertTo-Json
[
"one",
"two"
]
PS> '' | Select-Object @{ n='prop'; e={ @( 1, 2 ) } } | ConvertTo-Json
{
"prop": [
1,
2
]
}
'' | ForEach-Object { [PSCustomObject]@{ prop= @( 1, 2 ) }} | ConvertTo-Json
{
"prop": [
1,
2
]
}