【问题标题】:Set Value of Nested Object Property by Name in PowerShell在 PowerShell 中按名称设置嵌套对象属性的值
【发布时间】:2017-09-27 15:38:39
【问题描述】:

我想使用 PowerShell 设置嵌套对象属性的值。当您尝试设置第一级属性的值时,它非常简单:

$propertyName = "someProperty"
$obj.$propertyName = "someValue"  # ← It works

对于嵌套属性,它不起作用:

$propertyName = "someProperty.someNestedProperty"
$obj.$propertyName = "someValue"  # ← It doesn't work and raises an error.

如何使用PowerShell通过属性名称设置嵌套对象属性的值?

MCVE

对于那些想重现问题的人,这里有一个简单的例子:

$Obj= ConvertFrom-Json '{ "A": "x", "B": {"C": "y"} }'
# Or simply create the object:
# $Obj= @{ A = "x"; B = @{C = "y"} }
$Key = "B.C"
$Value = "Some Value"
$Obj.$Key = $Value

运行命令会报错:

“在此对象上找不到属性 'B.C'。请确认 属性存在,可以设置。”

注意:代码支持任何级别的嵌套。

【问题讨论】:

  • 它无法知道您要求的是嵌套属性。我试图找到我认为这是一个欺骗目标的东西。您需要构建逻辑以支持单个字符串中的嵌套属性。这会起作用,但不是你想要的$json.$propertyName.$nestedPropertyName.,因为它只会满足一个用例。需要一个递归函数iirc
  • 我想这就是我的想法stackoverflow.com/questions/45174708/…
  • @Matt 感谢您的评论。我知道$json.$propertyName.$nestedPropertyName。但它不是基于属性名称的,不满足运行时按名称解析属性的要求。还有关于链接的帖子,它是 Get,而我正在寻找 Set
  • 是的......我知道......看看链接的答案,看看是否能让你找到更好的方向。

标签: powershell anonymous-types cmdlet


【解决方案1】:

我创建了SetValueGetValue 函数,让您可以通过名称动态获取和设置对象(包括json 对象)的嵌套属性,它们可以完美运行!

它们是递归函数,通过拆分嵌套属性名称来解析复杂属性并逐步获取嵌套属性。

按名称获取嵌套属性的 GetValue 和 SetValue

# Functions
function GetValue($object, $key)
{
    $p1,$p2 = $key.Split(".")
    if($p2) { return GetValue -object $object.$p1 -key $p2 }
    else { return $object.$p1 }
}
function SetValue($object, $key, $Value)
{
    $p1,$p2 = $key.Split(".")
    if($p2) { SetValue -object $object.$p1 -key $p2 -Value $Value }
    else { $object.$p1 = $Value }
}

示例

在以下示例中,我使用SetValue 动态设置B.C,并使用GetValue 函数按名称获取其值:

# Example
$Obj = ConvertFrom-Json '{ "A": "x", "B": {"C": "y"} }'
# Or simply create the object:
# $Obj = @{ A = "x"; B = @{C = "y"} }
$Key = "B.C"
$Value = "Changed Dynamically!"
SetValue -object $Obj -key $Key -Value $Value
GetValue -object $Obj -key $Key

【讨论】:

  • 如果可以的话,我会支持这个+20。这有助于在 .net 项目的 appsettings.json 文件中设置连接字符串。
【解决方案2】:

我可以提议升级 Reza 的解决方案吗?使用此解决方案,您可以拥有多个级别的嵌套属性。

function GetValue($object, [string[]]$keys)
{
    $propertyName = $keys[0]
    if($keys.count.Equals(1)){
        return $object.$propertyName
    }
    else { 
        return GetValue -object $object.$propertyName -key ($keys | Select-Object -Skip 1)
    }
}


function SetValue($object, [string[]]$keys, $value)
{
    $propertyName = $keys[0]
    if($keys.count.Equals(1)) {
        $object.$propertyName = $value
    }
    else { 
        SetValue -object $object.$propertyName -key ($keys | Select-Object -Skip 1) -value $value
    }
}

用法

$Obj = ConvertFrom-Json '{ "A": "x", "B": {"C": {"D" : "y"}} }'
SetValue $Obj -key "B.C.D".Split(".") -value "z"
GetValue $Obj -key "B.C.D".Split(".")

【讨论】:

  • 感谢 Yves 的分享,我再次检查了我的答案,它支持任何级别的嵌套。
【解决方案3】:

您的 own solutions 有效,但不支持将索引访问作为嵌套属性访问路径的一部分(例如,B[1].C

一个简单的替代方法是使用Invoke-Expression (iex)。 虽然它should generally be avoided,但也有一些例外情况,它提供了最简单的解决方案,这就是其中之一:

假设您完全控制或隐式信任属性访问字符串:

$obj = ConvertFrom-Json '{ "A": "x", "B": [ {"C": "y"}, { "C": "z"} ] }'

$propPath = 'B[1].C'

# GET
Invoke-Expression "`$obj.$propPath" # -> 'z'

# SET
$value = 'Some Value'
Invoke-Expression "`$obj.$propPath = `$value" 

如果您信任输入,您可以避免不必要的命令注入,如下所示:

$safePropPath = $propPath -replace '(`)*\$', '$1$1`$$'
Invoke-Expression "`$obj.$safePropPath"
# ...

有关安全打包上述功能的便捷功能/ETS 方法,请参阅this answer

【讨论】:

    猜你喜欢
    • 2020-11-03
    • 2021-10-08
    • 1970-01-01
    • 1970-01-01
    • 2021-03-25
    • 2015-12-19
    • 2013-09-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多