Powershell 显示 JSON-object 和 ADDRESS-属性 的错误输出

Powershell showing wrong output for JSON-object and ADDRESS-property

我目前正在努力将 JSON 字符串转换为对象数组,并通常处理每个对象的 properties/attributes。

这是一个简单的演示,展示了例如属性"address"好像有点特殊:

cls
$json = '[{"id":"1","address":"1"},{"id":"2","address":"2"}]'
$list = $json | ConvertFrom-Json

$list.id                      # OK
$list.address                 # gives a weired result - is this a bug?
$list.GetEnumerator().address # that works

这是输出:

1
2

OverloadDefinitions                                                                                        
-------------------                                                                                        
System.Object&, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Address(int )  

1
2

如您所见,我需要添加“.GetEnumerator()”以获得正确的 "address" 值。

这是预期的吗?为了安全起见,我应该始终使用“.GetEnumerator()”吗?

因为 $list 是一个 array (集合),使用 $list.id 执行 member-access enumeration;也就是说,.id 属性 在每个元素 上自动访问 ,结果值作为数组返回 ([object[]])[1].

但是,如果数组/集合类型本身 有一个该名称的成员(属性 或方法),它 优先 ,这就是你的情况:.NET 数组有一个 .Address 方法(由运行时添加 - 参见 this answer),这就是抢占访问 元素的 .Address 属性.

(您看到的是 PowerShell 对方法签名(重载)的表示,这是您仅通过名称访问方法时得到的结果,而不是通过附加 ((...)) 实际调用它);试试 'foo'.ToUpper,例如。)

PowerShell 没有提供明确的运算符语法来区分直接成员访问和成员访问枚举是 [GitHub 问题 #7445](https://github.com/PowerShell/PowerShell/issues/7445) 的主题,但现有行为不太可能改变。

解决该问题的最有效方法是使用 PSv4+ .ForEach() 数组方法,该方法始终针对集合的 元素:

$list = '[{"id":"1","address":"1"},{"id":"2","address":"2"}]' | ConvertFrom-Json

$list.ForEach('address')

警告:如果您是 运行 Windows PowerShell$list 在某些情况下只能导致 单个 pscustomobject 而不是 数组 ,您必须 将数组子表达式运算符@(...)中的$list括起来,以确保.ForEach()方法可用。这是一个 [pscustomobject]-specific bug(假设即使是单个对象也应该始终有一个 .ForEach() 方法),它已在 PowerShell (Core) 6+.中修复

# @(...) is necessary in Windows PowerShell only.
@($list).ForEach('address')

注:

  • 从技术上讲,这个 returns 是一个 [System.Collections.ObjectModel.Collection[PSObject]] 集合,但在大多数情况下,您可以像使用常规 [object[]] PowerShell 数组一样使用它。

  • 与成员访问枚举不同,如果输入集合中只有 一个 元素,也会返回一个集合实例(而不是返回该元素的 属性 值原样,成员访问枚举的方式)。


PSv3- 中,您可以使用 ForEach-Object cmdlet,或 Select-Object -ExpandProperty:

$list | ForEach-Object address # PSv2: | ForEach-Object { $_.address }
# OR
$list | Select-Object -ExpandProperty address

[1] 如果集合中只有 one 元素,则其 属性 值按原样返回,不包含在 (单元素)数组,这与在变量中收集 pipeline 输出时应用的逻辑相同。 GitHub issue #6802.[=37= 中讨论了这种逻辑在 表达式 上下文的成员访问枚举的上下文中可能是意外的]