2

I have:

$method = "property.childproperty"

How can I get to $foo = $object.property.childproperty by using the $method variable?

0

3 Answers 3

1

One way to do it, split on . your string in $method then loop through each substring assigning, get the property value via dot-notation and assign it to a temporary variable:

$object = [pscustomobject]@{ property = [pscustomobject]@{ childproperty = 'hi' } }
$method = 'property.childproperty'

$tmp = $object
$method.Split('.') | ForEach-Object { $tmp = $tmp.$_ }
$tmp # hi

If you need to perform some validation, checking if such property exist, you can use .PSObject.Properties instead:

$object = [pscustomobject]@{ property = [pscustomobject]@{ childproperty = 'hi' } }
$method = 'property.notexist'

$tmp = $object
$method.Split('.') | ForEach-Object {
    if ($prop = $tmp.PSObject.Properties[$_]) {
        $tmp = $prop.Value
        return
    }

    throw "Property '$_' not found on '$tmp'...."
}

# Property 'notexist' not found on '@{childproperty=hi}'....

Another way to do it, could be via Invoke-Expression or ScriptBlock.Create. However, you should note that both approaches are unsafe if $method comes from an untrusted source.

Here is how it'd look:

$object = [pscustomobject]@{ property = [pscustomobject]@{ childproperty = 'hi' } }
$method = 'property.childproperty'
& ([scriptblock]::Create("`$object.$method")) # hi
Invoke-Expression "`$object.$method"          # hi

And here is why it could be dangerous if $method was from an arbitrary source:

$object = [pscustomobject]@{ property = [pscustomobject]@{ childproperty = 'hi' } }
$method = 'foo; Get-ChildItem'
& ([scriptblock]::Create("`$object.$method"))
Invoke-Expression "`$object.$method"
Sign up to request clarification or add additional context in comments.

Comments

0

Split the $method string into its constituent parts, then simply dereference the "next" step in the chain until you've reached the last one:

# sample input data
$foo = @{ property = @{ childproperty = 'value' }} 
$method = 'property.childproperty'

# split the path into individual property names
$steps = $method -split '\.'
# $cursor will refer to the value resolved in the previous step, starting with the target object $foo
$cursor = $foo

# keep resolving until there are no more steps/property names in the chain
while ($steps) {
    $next,$steps = $steps
    # overwrite previous value reference with whatever the next step resolves to
    $cursor = $cursor.$next
}

Write-Host "Resolved value is `$cursor`"

This pattern is easily abstracted into a re-usable function

Comments

0

Your question contains a very specific example where the solution has likely a lot of sub-questions, possible (code injection) pitfalls, and additional requirements. For this, I created a ObjectGraphTools module which has cmdlets available that might prevent you from these issues and reinventing the wheel.

To give you an idea of the features of the included Get-Node cmdlet:

Install-Module -Name ObjectGraphTools
$foo = @{ property = @{ childproperty = 42 }} 
$method = 'property.childproperty'
$foo | Get-Node $Method -Value
42

But due to the PowerShell member-access enumeration feature, your concerned property might not always be where you expect it to be, e.g.:

$foo = @{ property = @{ id = 7 }, @{ childproperty = 42 } }
$foo.property.childproperty
42

In which case you can't set it the same way you retrieve it:

$foo.property.childproperty = 421
InvalidOperation: The property 'childproperty' cannot be found on this object. Verify that the property exists and can be set.

Because the path to the concerned node is actually here:

$Node = $foo | Get-Node $Method
$Node

Path                      Name          Depth Value
----                      ----          ----- -----
property[0].childproperty childproperty 3     42

So, if you want to set the property, you need to use property[0].childproperty or do it via the found $Node:

$Node.Value = 421
$foo | ConvertTo-Expression -Expand 0
@{ property = @(@{ id = 7 }, @{ childproperty = 421 }) }

And in case one of the properties in the path contains a special character as e.g. a space, you might apply the common PowerShell quoting rules, like:

$foo = @{ property = @{ id = 7 }, @{ 'child property' = 42 } }
$foo | Get-Node property.'child property'

Path                         Name           Depth Value
----                         ----           ----- -----
property[1].'child property' child property 3     42

Anyways, together with the Extended Dot Notation (XDN) you might do things along with targeting deep descendants, use wildcard, select parents etc.
E.g.: you need the ID value of the sibling of the property that starts with child and has the value 42:

$foo | Get-Node ~child*=42...id -Value
7

Explanation:

  • ~: find any descendent property
  • child*: whare the name starts with child
  • =42: and has a value equal to 42
  • ...: get its grandparent
  • id: and then the -Value of its child called id

Note also that for all of these examples, it doesn't matter if it concern a hash table (dictionary) or a (PSCustom)Object or an array of any other list item.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.