使用变量间接访问 PSObject 属性 [英] Access PSObject property indirectly with variable
问题描述
假设我有这样的 JSON:
Say I have JSON like:
{
"a" : {
"b" : 1,
"c" : 2,
}
}
现在 ConvertTo-Json
会很高兴地从中创建 PSObjects
.我想访问一个我可以做的项目 $json.a.b
并获得 1 - 很好的嵌套属性.
Now ConvertTo-Json
will happily create PSObjects
out of that. I want to access an item I could do $json.a.b
and get 1 - nicely nested properties.
现在如果我有字符串 "a.b"
问题是如何使用该字符串访问该结构中的相同项目?似乎应该有一些我缺少的特殊语法,例如用于动态函数调用的 &
,否则您必须重复使用 Get-Member
自己解释字符串,我期望.
Now if I have the string "a.b"
the question is how to use that string to access the same item in that structure? Seems like there should be some special syntax I'm missing like &
for dynamic function calls because otherwise you have to interpret the string yourself using Get-Member
repeatedly I expect.
推荐答案
没有,没有特殊语法.
No, there is no special syntax.
虽然最简单的解决方案是使用 Invoke-Expression
,但如下所示,Invoke-Expression
通常应该避免;在这种特殊情况下是安全的,因为你完全控制输入字符串,但最好养成不使用Invoke-Expression
,特别是考虑到在大多数情况下都有更强大和更安全的替代方案:
While the simplest solution is to use Invoke-Expression
, as shown next, Invoke-Expression
should generally be avoided; it is safe in this particular scenario, because you fully control the input string, but it is better to form a habit of not using Invoke-Expression
, especially given that in most situations there are alternatives that are both more robust and more secure:
$json = @'
{
"a" : {
"b" : 1,
"c" : 2,
}
}
'@
$obj = ConvertFrom-Json $json
# The path to the target property.
$propertyPath = 'a.b'
# NOTE: In general, AVOID Invoke-Expression
# 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
.
The above is the equivalent of executing $obj.a.b.
directly and yields 1
.
或者,您可以编写一个简单的辅助函数:
Alternatively, you could write a simple helper function:
function propByPath($obj, $propertyPath) {
foreach ($prop in $propertyPath -split '\.') { $obj = $obj.$prop }
$obj # output
}
您将使用:
propByPath $obj $propertyPath
<小时>
您甚至可以使用 PowerShell 的 ETS(扩展类型系统)来将 .GetPropByPath()
方法附加到所有 [pscustomobject]
> 实例(PSv3+ 语法;在 PSv2 中,您必须创建一个 *.types.ps1xml
文件并使用 Update-TypeData 加载它 -PrependPath
):
You could even use PowerShell's ETS (extended type system) to attach a .GetPropByPath()
method to all [pscustomobject]
instances (PSv3+ syntax; in PSv2 you'd have to create a *.types.ps1xml
file and load it with 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')
.
注意:除了 System.Management.Automation.PSCustomObject
之外,还有目标类型 Deserialized.System.Management.Automation.PSCustomObject
还涵盖反序列化自定义对象,这些对象在许多场景中返回,例如使用 Import-CliXml
、从后台作业接收输出以及使用远程处理.
Note: Type Deserialized.System.Management.Automation.PSCustomObject
is targeted in addition to System.Management.Automation.PSCustomObject
in order to also cover deserialized custom objects, which are returned in a number of scenarios, such as using Import-CliXml
, receiving output from background jobs, and using remoting.
.GetPropByPath()
将在会话的剩余部分中的任何 [pscustomobject]
实例上可用(即使在 Update-TypeData 之前创建的实例上)
调用 [1]);放置 Update-TypeData
调用您的 $PROFILE
(配置文件)以使该方法默认可用.
.GetPropByPath()
will be available on any [pscustomobject]
instance in the remainder of the session (even on instances created prior to the Update-TypeData
call [1]); place the Update-TypeData
call in your $PROFILE
(profile file) to make the method available by default.
更强大的解决方案,支持索引并保留数组值属性等
A more robust solution that supports indexing and preserves array-valued properties as such
以上解决方法:
- 不支持将索引作为属性路径的一部分(例如,
'a.b[2]'
) - 使用管道逻辑展开数组值属性,这意味着单个元素数组被展开为其唯一的元素.
- doesn't support indices as part of the property path (e.g.,
'a.b[2]'
) - unwraps array-valued properties using pipeline logic, which means that a single-element array is unwrapped to its one and only element.
以下解决方案修复了这些限制,但请注意:
The following solution fixes these limitations, but note that:
仅支持文字、标量索引(也就是说,您可以使用
'ab[2]'
,但不能使用'ab[1..2]'
或'ab[1, 2]'
,例如)
Only literal, scalar indices are supported (that is, you can use
'a.b[2]'
, but not'a.b[1..2]'
or'a.b[1, 2]'
, for instance)
对于哈希表的属性,指定(文字)键名没有嵌入引用(例如,'a.ht[bar]'
);请注意,您通常无法访问数字哈希表键,此外,您将无法访问有序哈希表的条目索引.
For properties that are hashtables, specify the (literal) key name without embedded quoting (e.g., 'a.ht[bar]'
); note that you won't be able to access numeric hashtable keys in general, and, additionally, you won't be able to access an ordered hashtable's entries by index.
'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()
,即使 $co
是在调用 Update-TypeData
之前创建的,它也会输出 foo
.上>
[1] Verify with (all on one line) $co = New-Object PSCustomObject; Update-TypeData -TypeName System.Management.Automation.PSCustomObject -MemberType ScriptMethod -MemberName GetFoo -Value { 'foo' }; $co.GetFoo()
, which outputs foo
even though $co
was created before Update-TypeData
was called.
这篇关于使用变量间接访问 PSObject 属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!