Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么在转换JSON时,powershell在一行程序中给出的结果与在两行程序中给出的结果不同? 概述_Json_Powershell - Fatal编程技术网

为什么在转换JSON时,powershell在一行程序中给出的结果与在两行程序中给出的结果不同? 概述

为什么在转换JSON时,powershell在一行程序中给出的结果与在两行程序中给出的结果不同? 概述,json,powershell,Json,Powershell,在PowerShell3提示符下,我想调用一个RESTful服务,获取一些JSON,并将其打印出来。我发现,如果我将数据转换为powershell对象,然后将powershell对象转换回json,我会得到一个漂亮的打印字符串。但是,如果我将这两种转换组合成一个带有管道的内衬,我将得到不同的结果 TL;DR:这是: PS> $psobj = $orig | ConvertFrom-JSON PS> $psobj | ConvertTo-JSON 。。。给出了与此不同的结果: PS&

在PowerShell3提示符下,我想调用一个RESTful服务,获取一些JSON,并将其打印出来。我发现,如果我将数据转换为powershell对象,然后将powershell对象转换回json,我会得到一个漂亮的打印字符串。但是,如果我将这两种转换组合成一个带有管道的内衬,我将得到不同的结果

TL;DR:这是:

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
;但是,如果进一步的管道段处理对象,则区别很重要-请参见下文


问题行为是两个因素的组合:

  • convertfromjson
    通过管道将数组作为单个对象发送,从而偏离了正常的输出行为。也就是说,使用表示数组的JSON字符串,
    ConvertFrom JSON
    通过管道将生成的对象数组作为单个对象发送

    • 您可以验证
      convertfromjson
      的惊人行为,如下所示:

        PS> '[ "one", "two" ]' | ConvertFrom-Json | Get-Member
      
        TypeName: System.Object[]  # !! should be: System.String
        ...
      
    • 如果
      ConvertFrom-Json
      像cmdlet通常做的那样通过管道逐个传递其输出,
      Get-Member
      将返回集合中项目的(不同)类型,在本例中为
      [System.String]

      • (…)
        中包含命令将强制枚举其输出,这就是为什么
        ($orig | ConvertFrom Json)| ConvertTo Json
        是一种有效的解决方法
    • 这一行为是否应该改变(也仍然存在于PowerShell核心中)正在讨论中

  • 系统.Array
    类型-所有数组的基本类型-通过PowerShell的ETS(扩展类型系统-请参阅)
    为其定义了一个
    .Count
    属性,该属性导致
    转换为Json
    将该属性包含在其创建的Json字符串中,包含在同级
    属性中的数组元素

    • 只有当
      convertToJSON
      将数组作为一个整体
      视为输入对象时,才会发生这种情况,在本例中是由
      ConvertFrom Json
      生成的;e、 例如,
      ,(1,2)|converttojson
      解决了这个问题(一个嵌套数组,其内部数组作为单个对象发送),但是
      1,2 | converttojson
      没有(数组元素单独发送)

    • ETS提供的
      .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
        ]
      }