通过变量间接访问PSObject属性

时间:2018-08-15 17:05:41

标签: powershell

说我有JSON,如:

  {
    "a" : {
        "b" : 1,
        "c" : 2,
        }
  }

现在ConvertTo-Json会很高兴地从中创建PSObjects。我想访问一个我可以做的项目$json.a.b并获得1-很好地嵌套的属性。

现在,如果我有字符串"a.b",问题是如何使用该字符串访问该结构中的同一项目?似乎应该缺少一些特殊的语法,例如用于动态函数调用的&,因为否则,您必须自己反复使用Get-Member来解释字符串,我希望如此。

1 个答案:

答案 0 :(得分:6)

否,没有 no 特殊语法

最简单的解决方案是使用Invoke-Expression ,在这种受限制的情况下,这很好,但是Invoke-Expression should generally be avoided

$json = @'
{
  "a" : {
      "b" : 1,
      "c" : 2,
      }
}
'@

$obj = ConvertFrom-Json $json

# The path to the target property.
$propertyPath = 'a.b'

# Construct the expression and pass it to Invoke-Expression.
# Note the need to `-escape the `$` in `$obj` to prevent premature expansion.
Invoke-Expression "`$obj.$propertyPath"

以上等同于直接执行$obj.a.b.并产生1


或者,您可以编写一个简单的 helper函数:

function propByPath($obj, $propertyPath) {
  foreach ($prop in $propertyPath -split '\.')  { $obj = $obj.$prop }
  $obj # output
}

您将使用{p> 1代替Invoke-Expression

propByPath $obj $propertyPath

您甚至可以使用PowerShell的ETS (扩展类型系统).GetPropByPath()方法附加到所有[pscustomobject]实例 PSv3 + 语法;在PSv2中,您必须创建一个*.types.ps1xml文件并使用Update-TypeData -PrependPath加载它):

'System.Management.Automation.PSCustomObject',
'Deserialized.System.Management.Automation.PSCustomObject' |
  Update-TypeData -TypeName { $_ } `
                  -MemberType ScriptMethod -MemberName GetPropByPath -Value {                  #`
    param($propPath)
    $obj = $this
    foreach ($prop in $propPath -split '\.')  { $obj = $obj.$prop }
    $obj # output
  }

然后您可以呼叫$obj.GetPropByPath('a.b')

注意:除Deserialized.System.Management.Automation.PSCustomObject之外,还以类型System.Management.Automation.PSCustomObject为目标,以便涵盖以数字形式返回的反序列化自定义对象场景,例如使用Import-CliXml,从后台作业接收输出以及使用远程处理。

.GetPropByPath()将在其余会话中的任何[pscustomobject]实例上可用(甚至在Update-TypeData调用 [1] 之前创建的实例上) ;将Update-TypeData调用放入您的$PROFILE(配置文件)中,以使该方法默认可用。


一种更强大的解决方案,它支持索引并保留数组值属性

以上解决方案:

  • 不支持索引作为属性路径的一部分(例如'a.b[2]'
  • 使用管道逻辑解开数组值的属性,这意味着将单个元素数组解包为其唯一元素。

以下解决方案解决了这些限制,但请注意:

  • 仅支持文字标量索引(例如,您可以使用'a.b[2]',但不能使用'a.b[1..2]''a.b[1, 2]'

  • 对于作为哈希表的属性,请指定(文字)键名称​​,而无需嵌入引号(例如'a.ht[bar]');请注意,通常您将无法访问数字哈希表键,此外,您将无法通过访问有序哈希表的条目索引

'System.Management.Automation.PSCustomObject',
'Deserialized.System.Management.Automation.PSCustomObject' |
  Update-TypeData -TypeName { $_ } `
                  -MemberType ScriptMethod -MemberName GetPropByPath -Value {                  #`
    param($propPath)
    $obj = $this
    foreach ($prop in $propPath -split '\.')  {
      # See if the property spec has an index (e.g., 'foo[3]')
      if ($prop -match '(.+?)\[(.+?)\]$') {
        $obj = $obj.($Matches.1)[$Matches.2]
      } else {
        $obj = $obj.$prop
      }
    }
    # Output: If the value is a collection (array), output it as a
    #         *single* object.
    if ($obj.Count) {
      , $obj
    } else {
      $obj
    }
  }

[1]使用$co = New-Object PSCustomObject; Update-TypeData -TypeName System.Management.Automation.PSCustomObject -MemberType ScriptMethod -MemberName GetFoo -Value { 'foo' }; $co.GetFoo()(全部在一行上)进行验证,即使在调用foo之前创建了$co,它也会输出Update-TypeData