在PowerShell中通过名称设置嵌套对象属性的值

7

我希望使用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.,因为它只能满足那一个用例。如果我没记错的话,需要使用递归函数。 - Matt
我认为这就是我想到的 https://stackoverflow.com/questions/45174708/powershell-turn-period-delimited-string-into-object-properties/45175340#45175340 - Matt
@Matt 感谢您的评论。我知道$json.$propertyName.$nestedPropertyName,但它不是基于属性名称的,并且不能满足在运行时按名称解析属性的要求。关于链接的帖子,它是Get,而我正在寻找Set - Reza Aghaei
是的...我知道...不过看看链接中的答案,看看是否能让你更好地方向。 - Matt
3个回答

15

我创建了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

1
如果可以的话,我会给这个+20赞。这帮助我在一个.net项目中设置了appsettings.json文件中的连接字符串。 - longday

4

您的自己的解决方案很有效,但不支持嵌套属性访问路径中的索引访问(例如,B[1].C)。

一个简单的替代方案是使用Invoke-Expression (iex)。虽然通常应该避免使用它,但在某些特殊情况下,它提供了最简单的解决方案,这就是其中之一:

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

$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" 

如果您不信任输入,可以按照以下方式避免不必要的命令注入:[1]
$safePropPath = $propPath -replace '`|\$', '`$&'
Invoke-Expression "`$obj.$safePropPath"
# ...

如果需要一个方便的功能 / ETS方法,可以安全地打包上述功能,请参阅this answer


[1] 基于正则表达式-replace操作确保字符串中的任何$字符都被转义为`$,以防止它们被Invoke-Expression视为变量引用或子表达式;同样,现有的`实例也通过双倍化进行了转义。


3

我建议对 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的分享,我再次检查了我的答案,它支持任何层级的嵌套。 - Reza Aghaei

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接