Home > Mobile >  Set a PSObject path using an array for the "dot" variable names
Set a PSObject path using an array for the "dot" variable names

Time:02-04

I have a PSObject that I have filled with a json structure. I need to be able to set the value of one of the entries in the tree using an array that has the names nodes of the json path. Here is an example that gets close, but does not ultimately work (but helps explain what I am looking for):

$json = @"
{
    "Logging": {
      "LogLevel": {
        "Default": "Warning",
        "Microsoft": "Warning",
        "Microsoft.Hosting.Lifetime": "Information"
      }
    }
}
"@

$settings = $json | ConvertFrom-Json

[System.Collections.ArrayList] $jsonPath = New-Object -TypeName "System.Collections.ArrayList"
$jsonPath.Add("Logging") | Out-Null
$jsonPath.Add("LogLevel") | Out-Null
$jsonPath.Add("Microsoft") | Out-Null

Write-Output "Old Value was $($settings.$($jsonPath[0]).$($jsonPath[1]).$($jsonPath[2]))"

# I need a way to set this value when there could be an unknown number of elements in the array.
$settings.$($jsonPath[0]).$($jsonPath[1]).$($jsonPath[2]) = "Debug"

Write-Output "New Value is $($settings.$($jsonPath[0]).$($jsonPath[1]).$($jsonPath[2]))"

This works if I know that the $jsonPath array will have 3 elements. But it could have many more or less.

I thought to iterate the array like this:

$result = $settings
foreach ($pathItem in $jsonPath)
{
    $result = $result.$pathItem
}
$result = "Debug"

But this just sets the string value of $result. Not the value in $settings.

I feel like I need a way to get a reference of the $setting.$pathItem value (rather than the actual value), so that I can make sure I set that value on the $settings variable.

How can I update $settings using the values in the array as the dot de-referencers?

CodePudding user response:

Assuming you fully control or implicitly trust the content of array (list) $jsonPath, Invoke-Expression - which is generally to be avoided - offers a simple solution:

$jsonPath = 'Logging', 'LogLevel', 'Microsoft'

Invoke-Expression "`$settings.$($jsonPath -join '.') = 'Debug'"

Note: If there's a chance $jsonPath contains nonstandard property names (e.g. with spaces), use the following instead:

Invoke-Expression "`$settings.$($jsonPath.ForEach({ '{'   $_   '}' }) -join '.') = 'Debug'"

CodePudding user response:

Iterating through the path array is a sound option in my opinion, you only need to change your logic a bit in order to update the property:

$jsonPath = 'Logging\LogLevel\Microsoft'.Split('\')
$settings = $json | ConvertFrom-Json

$ref = $settings
foreach($token in $jsonPath) {
    # if this token is not the last in the array
    if($token -ne $jsonPath[-1]) {
        # we can safely get its value
        $ref = $ref.$token
        continue
    }
    # else, this is the last token, we need to update the property
    $ref.$token = 'newValue'
}

$settings.Logging.LogLevel.Microsoft # has newValue
  • Related