【问题标题】:Why does the pipeline not work on converted json objects?为什么管道不适用于转换后的 json 对象?
【发布时间】:2021-10-30 15:52:45
【问题描述】:

考虑以下代码:

$data = '[
    {
        "Name":  "banana",
        "Color":  "yellow"
    },
    {
        "Name":  "kiwi",
        "Color":  "green"
    },
    {
        "Name":  "apple",
        "Color":  "red"
    }
]'
# Returns 3 objects while only 1 was expected
$data | ConvertFrom-Json | Where-Object { $_.Name -eq 'banana' }

# Workaround, returns 1 object as expected:
($data | ConvertFrom-Json) | Where-Object { $_.Name -eq 'banana' }

为什么不能使用第一个选项?从 json 转换对象后,Where-Object 函数似乎不正确。这发生在 PowerShell 版本 5.1 上。

我们是否遗漏了一些明显的东西?

【问题讨论】:

  • 似乎某些 cmdlet 必须在使用前强制进行评估。 Get-Date 是另一个这样的难题。
  • 这也产生了一个有趣的结果。 $data | ConvertFrom-Json | Where-Object Name -eq 'banana'
  • 从 PowerShell v6 开始,convertfrom-json 不像其他 cmdlet 那样在传递到管道之前枚举 JSON 数组,而是沿着管道发送单个对象。使用(),您正在强制枚举。这已在 PowerShell v7 中修复

标签: powershell powershell-5.1


【解决方案1】:

与:

$data | ConvertFrom-Json | Where-Object { $_.Name -eq 'banana' }

会发生以下情况:

  1. ConvertFrom-Json 返回一个对象数组(它本身就是一个对象)。由于这是ConvertFrom-Json 返回的第一个(最终也是唯一的)“完成”对象,因此它作为一个整体沿管道传递。请记住,一个 cmdlet 通常可以返回多个对象数组。

  2. 因此,Where-Object 在这种情况下只接收一个对象(包含三个元素的整个数组)。 $_ 然后引用整个数组,而不是每个元素。因此,$_.Name 不会返回一个元素的名称,而是返回所有元素名称的列表。此外,在这种情况下,术语$_.Name -eq 'banana' 不是布尔表达式,而是元素名称的过滤列表(该列表仅包含“香蕉”)。只要列表不为空,Where-Object 就会将其评估为$true,因此您的整个数组(一个对象,具有三个元素)将进一步通过管道传输(在您的情况下打印)。因此,它不会像您假设的那样返回三个对象,而是返回一个包含三个对象的对象。

你的另一行对比:

($data | ConvertFrom-Json) | Where-Object { $_.Name -eq 'banana' }

好吧,简而言之,就是按照您的预期去做。为什么?因为圆括号打破了管道。由于括号,括号内的所有内容都将被完全评估,然后再进一步传递。在评估括号后,会有一个数组,它将被进一步传送。整个数组将逐个元素地通过管道传输。所以在这种情况下,Where-Object 会收到三个单独的对象,正如您所期望的那样。


另一个很好的例子是:

您无法覆盖当前正在阅读的文件:

Get-Content test.txt | Set-Content test.txt

但是你可以在读完文件后覆盖它:

(Get-Content test.txt) | Set-Content test.txt

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-10
    • 1970-01-01
    • 2010-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多