AnAverageDev
AnAverageDev

Reputation: 143

Add/Append a new key-value pair in JSON after reading from another JSON file in PowerShell

I have two JSON files with a bunch of configurations. When new custom configurations are added in globalconfigs.json, I want to add those key value pairs in another app1configs.json. Let me explain it a bit.

globalconfigs.json:

{
    "App1": {
       "files": [
         "app1configs.json"
       ],
       "values": {
         "NewConfigsBool": true,
         "NewConfigsValues": {
             "App1SampleConfigs": "CustomConfig1"
         },
         "CustomSQLFiles": true,
         "SQLPaths": [
              "D:\\some\\location\\to\\myconfigs.sql"
        ]
            }
        }

The above json code is a little snippet of the globalconfigurations.json. If the "NewConfigsBool":is true, I want to copy the key value pairs in "NewConfigsValues" to another JSON file which looks like this. App1 is one of many, however they all follow the same syntax. And each app has its own appconfigs.json.

app1configs.json:

{
    "globalsettings": {
        "values": {
            "sqlstring": "alkjsdaklsjdklajsd3910840294",
            "hostname": "somesite.local",
            "filepath": "D:\\dummy\\location"
        }
    },
       "files": [
            "mysettings.json",
            "executors.dll",
            "logrecords.txt"
        ]
}

The above code is how app1configs.json normally looks. However, once "NewConfigsBool" is true, app1configs.json should look like this:

{
    "globalsettings": {
        "values": {
            "sqlstring": "alkjsdaklsjdklajsd3910840294",
            "hostname": "somesite.local",
            "filepath": "D:\\dummy\\location",
            "App1SampleConfigs": "CustomConfig1"
        }
    },
    "files": [
        "mysettings.json",
        "executors.dll",
        "logrecords.txt"
    ],
    "SQLPaths": [
        "D:\\some\\location\\to\\myconfigs.sql"
    ]
}

These custom configurations are not always going to have the same "keys". The user enters the key and the value so it can change. I'd like to highlight the changes here too. Basically, the configurations in NewConfigsValues in globalconfigs.json are stored in globalsettings in app1configs.json. And the SQLPaths are stored as well like I have shown above.

I thought I could do text replacement here however, since the keys are not the same, I wasn't able to do text replacement.

I would very much appreciate help here or any kind of feedback.

Upvotes: 1

Views: 270

Answers (1)

filimonic
filimonic

Reputation: 4644

Solution Example:

It reads globalconfigs.json, loops through each application, checks if NewConfigsBool is true or false.

If NewConfigsBool is true, it loops through all application .files and adds or upodates %file%.globalsettings.values.%parameterName%.

The part for SQL is not implemented so it's similar and it's for you 😉

$root = 'S:\SCRIPTS\GC'
$backupNameFormat = '{0}.b{1}.bak'

$pathGS = [System.IO.Path]::Combine($root,'globalconfigs.json')


$GSData = [System.IO.File]::ReadAllText($pathGS) | ConvertFrom-Json 

$applicationNames = @($GSData | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty 'Name'  ) 
$applicationDataList = $applicationNames |
    ForEach-Object {
        [PSCustomObject]@{
            Name = $_
            Data = $GSData.($_)
        }
    }

forEach ($ad in $applicationDataList) {
    if (($null -ne $ad.Data) -and ($null -ne $ad.Data.values) -and ($ad.Data.values.NewConfigsBool -eq $true)) {
        $files = @($ad.Data.files)
        if ($files.Count -le 0) {
            Write-Warning "For application $($ad.Name) new config is requested, but no files to patch"
        } else {
            foreach ($fName in $files) {
                $fPath = [System.IO.Path]::Combine($root, $fName)
                if (-not [System.IO.File]::Exists($fPath)) {
                    Write-Warning "For application $($ad.Name) new config is requested, but no file $($fPath) can not be found"
                } else {
                    $fData = $null
                    try {
                        $fData = [System.IO.File]::ReadAllText($fPath) | ConvertFrom-Json -ErrorAction Stop
                    } catch {
                        Write-Warning "For application $($ad.Name) new config is requested, but file $($fPath) can not be read: $($_.Exception.Message)"
                    }
                    if (($null -ne $fData) -and ($null -ne $fData.globalsettings) -and ($null -ne $fData.globalsettings.values)) {
                        if ($null -ne $ad.Data.values.NewConfigsValues) {
                            $newValues = $ad.Data.values.NewConfigsValues
                            $newValueNames = @($newValues | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty 'Name')
                            $hasErrors = $false
                            try {
                                $newValueNames | 
                                    % {Add-Member -InputObject $fData.globalsettings.values -MemberType NoteProperty -Name $_ -Value ($newValues.($_)) -Force -ErrorAction Stop}
                            } catch {
                                $hasErrors = $true
                                Write-Warning  "For application $($ad.Name) new config application failed for file $($fPath): $($_.Exception.Message)"
                            }

                            if ($hasErrors -eq $false) {
                                $backupName = [String]::Format($backupNameFormat, [System.IO.Path]::GetFileName($fPath), [DateTime]::UtcNow.ToString('yyyy_MM_dd.HH_mm_ss'))
                                $backupPath = [System.IO.Path]::Combine($root,$backupName)
                                $backupComplete = $false
                                try {
                                    [System.IO.File]::Copy($fPath, $backupPath, $false)
                                    $backupComplete = $true
                                } catch {
                                    Write-Warning "Unable to backup file $($fPath) to $($backupPath): $($_.Exception.Message)"
                                    $backupComplete = $false
                                }
                                if ($backupComplete -eq $true) {
                                    try {
                                        $fData | ConvertTo-Json -Depth 100 | Out-File -FilePath $fPath -Force -Confirm:$false -ErrorAction Stop
                                    } catch {
                                        Write-Warning -Message "Unable to save file $($fPath): $($_.Exception.Message)"
                                    }
                                }
                            }
                        } else {
                            Write-Warning -Message "For application $($ad.Name) new config is requested, but file $($fPath) can not be patched because `"NewConfigsValues`" is empty"
                        }
                    }

                }
            }
        }
    } else {
        Write-Verbose "Ignoring application $($ad.Name) configuration because it's `"values.NewConfigsBool`" NE True"
    }
}

Upvotes: 1

Related Questions