1

I'm trying to edit an object in PowerShell that was created through importing a JSON file. I'm then trying to use dot notation to access or amend properties of the object.

For example, below is a JSON sample and a code snippet

{
  "menus": {
    "menu1": {
        "position": "left"
    }
  }
}
# Import the file
$settings = Get-Content -Path somefile.json | ConvertFrom-Json

# Specify the property to change
$valueToChange = "menus.menu1.position"
$value = "left"

# Set the appropriate value according to the variables above
$settings.$valueToChange = $value

The above gives me:

Exception setting "menus.menu1.position": "The property 'menus.menu1.position' cannot be found on this object. Verify that the property exists and can be set."."

Whereas, if I do the exact same without the variables in the dot notation, it works.

# Import the file
$settings = Get-Content -Path somefile.json | ConvertFrom-Json

# Specify the property to change
$value = "left"

# Set the appropriate value according to the variables above but with a hard coded dot notation
$settings.menus.menu1.position = $value

I'm going to guess that it's related to the variable being a string or similar but the issue is that I need to programmatically calculate the dot notation for setting the values in the file, or at least allow a user to specify it.

I've actually worked around the issue now (by just avoiding this approach entirely) but I wanted to see if this was possible.

Even a clean alternative to dot notation would be good to see. The only reason I went with it in the first place is because my real JSON object is quite complicated and the dot notation is very straightforward and easy to follow.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
DL3001
  • 77
  • 2
  • 10
  • 1
    Related question that covers the aspect of _getting_ a nested property value by a string containing a property path: https://stackoverflow.com/q/51863251/45375 – mklement0 Nov 12 '19 at 18:29

3 Answers3

2

What you're trying to do there is not possible in PowerShell unless you use Invoke-Expression (which is strongly discouraged).

What you could do is use a recursive function, e.g. like this:

function Set-Property($Json, $Path, $Value) {
    $first, $rest = $Path
    if ($rest) {
        Set-Property -Json $Json.$first -Path $rest -Value $Value
    } else {
        $Json.$first = $Value
    }
}

$valueToChange = 'menus.menu1.position'.Split('.')
$newValue = 'left'

Set-Property -Json $settings -Path $valueToChange -Value $newValue

Note that this is just a very basic draft with no error handling whatsoever.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
  • Thanks a lot for this, I went with the recursive function approach you suggested above almost exactly, just with a couple of small changes. I didn't realise this was a duplicate, I guess because I didn't phrase my question in a similar way to the other... Apologies. – DL3001 Nov 12 '19 at 10:05
  • 1
    Nicely done (you already had my +1). You could even consider calling the function `Set-Property`, because it should work with property paths for _any_ object. – mklement0 Nov 12 '19 at 18:09
-1

This seems to work (EDIT: I later found it doesn't):

$settings = Get-Content -Path somefile.json | ConvertFrom-Json
$valueToChange = "menus.menu1.position"
$value = "right"
set-variable settings.$valuetochange $value
get-variable settings.$valuetochange


Name                           Value
----                           -----
settings.menus.menu1.position  right

Or not:

dir variable:sett*

Name                           Value
----                           -----
settings                       @{menus=}
settings.menus.menu1.position  right

This works, although I'm not sure of its convenience:

$value = 'right'
$prop1,$prop2,$prop3 = echo menus menu1 position
$settings.$prop1.$prop2.$prop3 = $value
$settings.menus

menu1
-----
@{position=right}
js2010
  • 23,033
  • 6
  • 64
  • 66
  • The OP wants to update the imported JSON data, not define a variable named after a particular path in the JSON object tree. – Ansgar Wiechers Nov 11 '19 at 15:30
  • @AnsgarWiechers I thought it would work. I added another way. – js2010 Nov 11 '19 at 17:48
  • 1
    Your first approach cannot work b/c otherwise it would be impossible to have property names with dots in them. Your second approach works for this particular example, but from my understanding the OP wants to be able to specify an arbitrary path. – Ansgar Wiechers Nov 11 '19 at 17:56
-1

Wrapping the variable syntax with $() or () works in 5.1 and later. Example:

$settings = Get-Content -Path "jsonData.json" | ConvertFrom-Json

$p1 = "menus"
$p2 = "menu1"
$p3 = "position"
$newVal = "right"

write-host $settings.$($p1).$($p2).$($p3)
$settings.$($p1).$($p2).$($p3) = $newVal
write-host $settings.$($p1).$($p2).$($p3)

----------------------OR-------------------------

write-host $settings.($p1).($p2).($p3)
$settings.($p1).($p2).($p3) = $newVal
write-host $settings.($p1).($p2).($p3)
  • How does this answer the question? – Lance U. Matthews Nov 05 '21 at 23:59
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Nov 06 '21 at 01:16
  • The original response just demonstrated what you needed, which is to wrap the dot accessor value with $(). I modified the answer to your specific issue. – Kyle Knocke Jan 18 '22 at 21:38
  • I believe that () should work as well, it did at least for this snippet - however, I have run into issues in which $() is required in previous projects. – Kyle Knocke Jan 18 '22 at 22:26