首页 > 解决方案 > Powershell 显示 JSON-object 和 ADDRESS-property 的错误输出

问题描述

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

这是一个简单的演示,显示例如属性“地址”似乎有点特殊:

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()”以获得正确的“地址”值。

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

标签: jsonpowershellconvertfrom-jsonmember-enumeration

解决方案


由于$list是一个数组(集合),使用类似$list.id执行成员枚举的东西;也就是说,在每个元素上.id自动访问该属性,并将结果值作为数组 ( ) [1]返回。[object[]]

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

(您看到的是 PowerShell 对方法签名(重载)的表示,这是您仅按名称访问方法时得到的,而实际上没有通过附加 ( (...)) 来调用它; try 'foo'.ToUpper,fo 实例。)

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]特定的错误(假设即使是单个对象也应该始终有一个.ForEach()方法),已在 PowerShell (Core) 6+ 中修复

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

笔记:

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

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


PSv3-中,您可以使用ForEach-Object cmdletSelect-Object -ExpandProperty

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

[1] 如果集合中只有一个元素,则其属性值按原样返回,而不是包装在(单元素)数组中,这与在变量中收集管道输出时应用的逻辑相同。这个逻辑在成员枚举的上下文中可能是意料之外的,这是一个表达式上下文,正在这个 GitHub issue中讨论。


推荐阅读