How to replace a property in a json file in powershell

I'm having a difficult time reading in a JSON file in powershell, replacing a value and writing back to the file or a file.

Given the following:

[Object]$QuickJson = @'
{
    "swagger": "1.0",
    "info": {
        "version": "0.0.1",
        "title": "this is a title",
        "description": "this is a description"
    },
    "basePath": "/test",   
    "paths" : {
        "/GetSomething": {
            "get": {
                "name" : "test01"
            }
        },
        "/GetSomethingElse" : {
            "get": {
                "name" : "test02"
            }
        },
        "/GetAnotherThing": {
            "get": {
                "name" : "test03"
            }
        }
    }
}
'@

What I'm interested in here is replacing the values in the "paths" object with something else. I read the file and can access the object however with what I'm trying:

[object]$MyPSJson = ConvertFrom-Json -InputObject $QuickJson

foreach($info in $MyPSJson.paths.PSObject.Properties)
{
    $path = $info.Name
    Write-Host "path: $path"
    $info.Name = "$path?code=fsfsfsfsfsfdfds"

    Write-Host $info.Name 
}

I don't know what those "paths" will be, I need to take the existing value and append a code value to it so I need to be able to iterate through all the paths and do this replacement. When I try the above I get an error:

path: /GetSomething -- 'Name' is a ReadOnly property. At C:\scripts\test.ps1:44 char:5 + $info.Name = "$path?code=fsfsfsfsfsfdfds" + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : InvalidOperation: (:) [], RuntimeException + FullyQualifiedErrorId : PropertyAssignmentException

I've tried several different things and yet to come up with a good, or workable for that matter, solution. Any help or pointing in the right direction is greatly appreciated.

3 answers

  • answered 2018-10-11 23:23 TheMadTechnician

    You could do something very similar to what you have now, just instead of changing the existing properties you record the initial properties, add new ones with the modifications you want, then set $MyPSJson.paths to itself excluding the old properties, so all it has are the new properties.

    #Find initial paths
    $Paths=$MyPSJson.paths.psobject.Properties.Name
    
    #Add new paths with the modification to the name, and set the value to the same as the old path
    $Paths|%{Add-Member -InputObject $MyPSJson.paths -NotePropertyName "$_`?code=fsfsfsfsfsfdfds" -NotePropertyValue $MyPSJson.paths.$_}
    
    #Set the paths object to itself, excluding the original paths
    $MyPSJson.paths = $MyPSJson.paths|Select * -ExcludeProperty $Paths
    

  • answered 2018-10-12 04:20 mklement0

    Try the following (PSv3+):

    $MyPSJson.paths = $MyPSJson.paths.psobject.properties |
      ForEach-Object { $renamedProps = [ordered] @{} } { 
        $renamedProps[$_.Name + '?foo=bar&baz=bam'] = $_.Value
      } { [pscustomobject] $renamedProps }
    

    Of technically necessity, this recreates the .paths object with modified property names.

    It does so by enumerating the original properties, adding their values under the modified name to an ordered hashtable, which on completion of the pipeline is converted to a custom object ([pscustomobject]).


    As for what you tried:

    A given object's properties cannot be renamed - only its properties' values can be changed.

    Therefore, you must create a new object with the desired new property names and the same values as the original.

    As an aside: an [object] cast is pointless in PowerShell - it is an effective no-op.
    By contrast, a [pscustomobject] cast can be used to create [pscustomobject] instances from [ordered] hashtables, which is the technique used above; casting from other types is again a virtual no-op.

  • answered 2018-10-12 18:03 so cal cheesehead

    In addition to the accepted answer which worked for me I also found another way which was initially suggested which was to use a hashtable:

    function Convert-JsonFileToHashTable([string]$file) {
        $text = [IO.File]::ReadAllText($file)
        $parser = New-Object Web.Script.Serialization.JavaScriptSerializer
        $parser.MaxJsonLength = $text.length
        $hashtable = $parser.Deserialize($text, @{}.GetType())
    
        return $hashtable
    }
    
    $hashtable = Convert-JsonFileToHashTable (Resolve-Path -Path $DefinitionFile)
    
    $keys = New-Object System.Collections.ArrayList($null)
    $keys.AddRange($hashtable["paths"].Keys)
    foreach ($key in $keys) 
    {
        $newPath = $key + "?code=$Code"
        $hashtable["paths"].$newPath = $hashtable["paths"].$key
        $hashtable["paths"].Remove($key) | Out-Null
    }
    
    $json = $hashtable | ConvertTo-Json -Depth 20
    Write-Output $json | Out-File $newFile -Encoding utf8