What I'm trying to do is take any well-formed JSON file/object and search for a path inside it. If the path isn't found, move on. If it is found, update the value. Once updated, save the updated JSON to the original file.
The catch to this, is the well-formed JSON structure is not known ahead of time. It's possible I might be searching hundreds of .json files on a disk, so the files that don't match any of my search terms can just be ignored.
I'm struggling to wrap my head around how to solve this problem. Most of the examples out there don't have a JSON object with an array for one of the key values, or they don't access the properties dynamically when an array is involved.
This link: Powershell: How to Update/Replace data and values in Json and XML Object shows a (sort of)"real" JSON structure, but the accepted answer relies on knowing what the JSON structure is (the OP didn't ask for help with dynamic pathing).
This link: Set Value of Nested Object Property by Name in PowerShell has something very close, although when an array is in the mix, it doesn't work properly when setting.
Here's some example JSON to use with this problem, though again, the structure is not known before the script runs. I'm looping over a list of files on disk, and executing for each file.
$JSON = ConvertFrom-Json '{
"key1":"key 1 value",
"options":{
"outDir":"./app-dir",
"lib":[
"someLibrary",
"anotherLibrary"
],
"someObjects":[
{
"first":"I am first"
},
{
"second":"I am second"
}
]
}
}'
The string to search this json might look like the following:
$SearchString = 'options.someObjects.first'
Or perhaps, something non-existent like:
$SearchString = 'options.someObjects.foo'
Using the recursive function GetValue from the 2nd article works beautifully for getting (and much more elegant than what I was doing):
function GetValue($object, $key)
{
$p1,$p2 = $key.Split(".")
if($p2) { return GetValue -object $object.$p1 -key $p2 }
else { return $object.$p1 }
}
However, the function SetValue does not work with an array. It returns an error stating "The property 'first' can not be found on this object."
function SetValue($object, $key, $Value)
{
$p1,$p2 = $key.Split(".")
if($p2) { SetValue -object $object.$p1 -key $p2 -Value $Value }
else { $object.$p1 = $Value }
}
I am aware this is because $JSON.options.someObjects is an array, therefore to access the object with the "first" key, the path would be:
$JSON.options.someObjects[0].first
That's the problem I'm having. How do I dynamically iterate over all objects once it reaches a part of the path that needs iterating? That part of the path could be anywhere, or more levels down, etc...
It's strange that powershell will allow you to dynamically iterate through an array when getting the value, but not when trying to set it.
Here's a complete example which demonstrates the entire issue:
#Create JSON:
$JSON = ConvertFrom-Json '{
"key1":"key 1 value",
"options":{
"outDir":"./app-dir",
"lib":[
"someLibrary",
"anotherLibrary"
],
"someObjects":[
{
"first":"I am first"
},
{
"second":"I am second"
}
]
}
}'
$SearchPath = 'options.someObjects.first'
$NewValue = 'I am a new value'
function GetValue($object, $key)
{
$p1,$p2 = $key.Split(".")
if($p2) { 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 { return $object.$p1 = $Value }
}
GetValue -object $JSON -key $SearchPath
SetValue -object $JSON -key $SearchPath -Value $NewValue
I've been searching all kinds of different terms trying to arrive at a good solution for this problem, but so far, no luck. I'm fairly certain I'm not the 1st person to want to do this sort of thing, apologies if I missed the answer somewhere.