3

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.

so cal cheesehead
  • 2,515
  • 4
  • 29
  • 50
  • are you just changing the path name? – ArcSet Oct 11 '18 at 19:54
  • The `Name` field is of type `System.Management.Automation.PSNoteProperty` which is read-only. You would need to add extra members to the `paths` child. – Maximilian Burszley Oct 11 '18 at 19:55
  • @ArcSet Yes I only need to change the path name – so cal cheesehead Oct 11 '18 at 19:55
  • @TheIncorrigible1 Yes that's the problem i'm running into which is why i believe/know this isn't a correct approach and should probably be doing it some other way just not sure how to go about it – so cal cheesehead Oct 11 '18 at 19:56
  • To do what you want with this, I suspect you'll need to convert the object into a hashtable and then do your replace/convert back to json. – Maximilian Burszley Oct 11 '18 at 19:56
  • @TheIncorrigible1 thanks I'll look into that see if it helps me resolve the issue – so cal cheesehead Oct 11 '18 at 20:01
  • Are you trying to change "/GetSomething" or "test01" – ArcSet Oct 11 '18 at 20:01
  • @ArcSet They're trying to change `/GetSomething` into `/GetSomething?code=fsfsfd` – Maximilian Burszley Oct 11 '18 at 20:01
  • @TheIncorrigible1 Thank you :) – ArcSet Oct 11 '18 at 20:02
  • @ArcSet I'm trying to change "/GetSomething" into "/GetSomething?code=fsf",everything else in the file should remain the same – so cal cheesehead Oct 11 '18 at 20:03
  • Here are [two questions](https://stackoverflow.com/questions/3740128/pscustomobject-to-hashtable) that [should give you direction](https://stackoverflow.com/questions/22002748/hashtables-from-convertfrom-json-have-different-type-from-powershells-built-in-h) – Maximilian Burszley Oct 11 '18 at 20:11
  • as @TheIncorrigible1 mentioned, my solution does not achieve what OP is looking for. I am deleting that answer for now. The name property of those values cannot be changed during runtime as stated above. You will need to do conversions to manipulate the data and back. TheIncorrigible1 is putting you in the right path. – Peter Kay Oct 11 '18 at 21:54

3 Answers3

2

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
TheMadTechnician
  • 34,906
  • 3
  • 42
  • 56
1

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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
0

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
so cal cheesehead
  • 2,515
  • 4
  • 29
  • 50