0

For context, I am attempting to create a cmdlet that would allow for single value substitutions on arbitrary Json files, for use in some pipelines. I've managed to get this working for non-array-containing Json.

A representative bit of Json:

{"test": {
    "env": "dev",
    "concept": "abstraction",
    "array": [
      {"id":1, "name":"first"},
      {"id":2, "name":"second"}
    ]
  }
}

I want to be able to replace values by providing a function with a path like test.array[1].name and a value.

After using ConvertFrom-Json on the Json above, I attempt to use the following function (based on this answer) to replace second with third:

function SetValue($object, $key, $value) {
    $p1, $p2 = $key.Split(".")
    $a = $p1 | Select-String -Pattern '\[(\d{1,3})\]'
    if ($a.Matches.Success) {
        $index = $a.Matches.Groups[1].Value
        $p1 = ($p1 | Select-String -Pattern '(\w*)\[').Matches.Groups[1].Value
        if ($p2.length -gt 0) { SetValue -object $object.$p1[$index] -key $p2 -value $value }
        else { $object.$p1[$index] = $value }
    }
    else {

        if ($p2.length -gt 0) { SetValue -object $object.$p1 -key $p2 -value $value }
        else {
            Write-Host $object.$p1
            $object.$p1 = $value
        }
    }
}
$content = SetValue -object $content -key "test.array[1].name" -rep "third"

Unfortunately this results in the following:

{ "test": {
    "env": "dev",
    "concept": "abstraction",
    "array": [
      "@{id=1; name=first}",
      "@{id=2; name=third}"
    ]
  }
}

If the values in the array aren't objects the code works fine as presented, it's only when we get to objects within arrays that this output happens.

What would be a way to ensure that the returned Json contains an array that is more in line with the input?

Edit: please note that the actual cause of the issue lay in not setting the -Depth property of ConvertTo-Json to 3 or greater. Doing so restored the resulting Json to the expected format. The accepted answer was still helpful in investigating the cause.

  • Can you please add an example of a set of data that does not behave like you want it to? – Otter Jul 20 '21 at 20:09
  • Hi, the data is not the issue. In both my code and the one from mklement0 in the answer below, it is the resulting json, post replacement, that is invalid (as in, not parsable with `ConvertFrom-Json`) due to some issue with either accessing or modifying the object within the array. – Pedro Bento Jul 21 '21 at 00:47

1 Answers1

0

While Invoke-Expression (iex) should generally be avoided, there are exceptional cases where it offers the simplest solution.

$fromJson = @'
{ 
  "test": {
    "env": "dev",
    "concept": "abstraction",
    "array": [
      {"id":1, "name":"first"},
      {"id":2, "name":"second"}
    ]
  }
}
'@ | ConvertFrom-Json

$nestedPropertyAccessor = 'test.array[1].name'

$newValue = 'third'

Invoke-Expression "`$fromJson.$nestedPropertyAccessor = `"$newValue`""

Important:

  • Be sure that you either fully control or implicitly trust the content of the $nestedPropertyAccessor and $newValue variables, to prevent inadvertent or malicious execution of injected commands.

  • On re-conversion to JSON, be sure to pass a high-enough -Depth argument to ConvertTo-Json; with the sample JSON, at least -Depth 3 is required - see this post.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • My pleasure, @PedroBento, and thanks for pointing out the `-Depth` issue on re-conversion to JSON - please see my update, which points out the situational need for a `-Depth` argument and links to a post with further information. – mklement0 Jul 21 '21 at 01:54