假设我有这样的 JSON:

  { 
    "a" : { 
        "b" : 1, 
        "c" : 2 
        } 
  } 
现在 ConvertTo-Json将愉快地创造 PSObjects出于那个。我想访问我可以做的项目 $json.a.b并获得 1 - 很好的嵌套属性。
现在,如果我有字符串 "a.b"问题是如何使用该字符串访问该结构中的相同项目?似乎应该有一些我缺少的特殊语法,例如 &用于动态函数调用,否则您必须使用 Get-Member 自己解释字符串我反复期待。

请您参考如下方法:

不,还有没有特殊语法 ,但有一个简单的 解决方法 , 使用 iex , Invoke-Expression 的内置别名[1]小命令:

$propertyPath = 'a.b' 
 
# Note the ` (backtick) before $json, to prevent premature expansion. 
iex "`$json.$propertyPath" # Same as: $json.a.b 
 
# You can use the same approach for *setting* a property value: 
$newValue = 'foo' 
iex "`$json.$propertyPath = `$newValue" # Same as: $json.a.b = $newValue 
警告 : 这样做 仅当您完全控制或隐式信任 $propertyPath 的值时 .
仅在极少数情况下是 Invoke-Expression 真正需要,它 should generally be avoided ,因为它可能存在安全风险。
请注意 如果目标属性包含特定集合类型的实例并且您希望按原样保留它(这并不常见) (例如,如果属性值是强类型数组,例如 [int[]] ,或列表类型的实例,例如 [System.Collections.Generic.List`1] ),请使用以下内容:
# "," constructs an aux., transient array that is enumerated by 
# Invoke-Expression and therefore returns the original property value as-is. 
iex ", `$json.$propertyPath" 
没有 , 技术, Invoke-Expression枚举集合值属性的元素,您最终会得到一个常规的 PowerShell 数组,其类型为 [object[]] - 但是,通常情况下,这种区别无关紧要。
注意:如果您要发送 , 的结果技术直接通过管道,集合值的属性值将作为单个对象发送,而不是像往常一样被枚举。 (相比之下,如果您先将结果保存在变量中,然后通过管道将其发送,则会发生通常的枚举)。虽然您可以简单地通过包含 Invoke-Expression 来强制枚举调用 (...) ,没有理由使用 , 考虑到枚举总是会丢失有关正在枚举其元素的集合类型的信息。
继续阅读打包解决方案。

笔记:
  • 以下打包解决方案原用 Invoke-Expression结合清理指定的属性路径,以防止无意/恶意注入(inject)命令。但是,这些解决方案现在使用不同的方法,即将属性路径拆分为单独的属性名称并迭代地深入到对象中,如 Gyula Kokas's helpful answer 所示。 .这不仅不需要 sanitizer ,而且比使用 Invoke-Expression 更快。 (后者仍然值得考虑一次性使用)。
  • 此技术的简洁、仅获取、始终枚举的版本将是以下函数:
    # Sample call: propByPath $json 'a.b' 
    function propByPath { param($obj, $propPath) foreach ($prop in $propPath.Split('.')) { $obj = $obj.$prop }; $obj } 
    
  • 下面更详细的解决方案提供了什么:参数验证,还可以通过路径设置属性值,以及 - 在 propByPath 的情况下function - 防止枚举作为集合的属性值的选项(请参阅下一点)。

  • propByPath函数提供了 -NoEnumerate切换到可选地请求保留属性值的特定集合类型。
  • 相比之下,.PropByPath() 中省略了此功能。方法,因为没有语法上方便的方式来请求它(方法只支持位置参数)。一个可能的解决方案是创建第二种方法,比如 .PropByPathNoEnumerate() ,这适用于 , 上面讨论的技术。

  • 辅助函数 propByPath :
    function propByPath { 
     
      param( 
        [Parameter(Mandatory)] $Object, 
        [Parameter(Mandatory)] [string] $PropertyPath, 
        $Value,               # optional value to SET 
        [switch] $NoEnumerate # only applies to GET 
      ) 
     
      Set-StrictMode -Version 1 
     
      # Note: Iteratively drilling down into the object turns out to be *faster* 
      #       than using Invoke-Expression; it also obviates the need to sanitize 
      #       the property-path string. 
       
      $props = $PropertyPath.Split('.') # Split the path into an array of property names. 
      if ($PSBoundParameters.ContainsKey('Value')) { # SET 
        $parentObject = $Object 
        if ($props.Count -gt 1) { 
          foreach ($prop in $props[0..($props.Count-2)]) { $parentObject = $parentObject.$prop } 
        } 
        $parentObject.($props[-1]) = $Value 
      } 
      else { # GET 
        $value = $Object 
        foreach ($prop in $props) { $value = $value.$prop } 
        if ($NoEnumerate) { 
          , $value 
        } else { 
          $value 
        } 
      } 
     
    } 
    
    而不是 Invoke-Expression然后调用您将使用:
    # GET 
    propByPath $obj $propertyPath 
     
    # GET, with preservation of the property value's specific collection type. 
    propByPath $obj $propertyPath -NoEnumerate 
     
     
    # SET 
    propByPath $obj $propertyPath 'new value' 
    

    你甚至可以 使用 PowerShell 的 ETS (扩展类型系统)到 附上 .PropByPath()所有人的方法[pscustomobject]实例 ( PSv3+ 语法;在 PSv2 中,您必须创建一个 *.types.ps1xml 文件并使用 Update-TypeData -PrependPath 加载它):
    'System.Management.Automation.PSCustomObject', 
    'Deserialized.System.Management.Automation.PSCustomObject' | 
      Update-TypeData -TypeName { $_ } ` 
                      -MemberType ScriptMethod -MemberName PropByPath -Value  {                  #` 
     
                        param( 
                          [Parameter(Mandatory)] [string] $PropertyPath, 
                          $Value 
                        ) 
                        Set-StrictMode -Version 1 
     
                         
                        $props = $PropertyPath.Split('.') # Split the path into an array of property names. 
                        if ($PSBoundParameters.ContainsKey('Value')) { # SET 
                            $parentObject = $this 
                            if ($props.Count -gt 1) { 
                              foreach ($prop in $props[0..($props.Count-2)]) { $parentObject = $parentObject.$prop } 
                            } 
                            $parentObject.($props[-1]) = $Value 
                        } 
                        else { # GET 
                          # Note: Iteratively drilling down into the object turns out to be *faster* 
                          #       than using Invoke-Expression; it also obviates the need to sanitize 
                          #       the property-path string. 
                          $value = $this 
                          foreach ($prop in $PropertyPath.Split('.')) { $value = $value.$prop } 
                          $value 
                        } 
     
                      } 
    
    然后您可以拨打 $obj.PropByPath('a.b')$obj.PropByPath('a.b', 'new value') 备注 : 类型 Deserialized.System.Management.Automation.PSCustomObject除了 System.Management.Automation.PSCustomObject为了还涵盖反序列化的自定义对象,这些对象在许多场景中返回,例如使用 Import-CliXml ,从后台作业接收输出,并使用远程处理。 .PropByPath()将在任何 [pscustomobject] 上提供 session 剩余部分的实例(即使是在 Update-TypeData 调用 [2] 之前创建的实例);放置 Update-TypeData 调用您的 $PROFILE (配置文件)使该方法默认可用。

    [1] 注意:虽然通常建议将别名限制为交互式使用并在脚本中使用完整的 cmdlet 名称,但使用 iex对我来说是可以接受的,因为它是一个内置别名并且可以提供简洁的解决方案。
    [2] 验证(全部在一行) $co = New-Object PSCustomObject; Update-TypeData -TypeName System.Management.Automation.PSCustomObject -MemberType ScriptMethod -MemberName GetFoo -Value { 'foo' }; $co.GetFoo() ,输出 foo即使 $co之前创建的 Update-TypeData被称为。


    评论关闭
    IT序号网

    微信公众号号:IT虾米 (左侧二维码扫一扫)欢迎添加!