The JSON file structure looks like below. I want to change the points value with PowerShell. In id 1, it should be a positive value, 48-35 = output 13, and in id 2, it should be a negative value, 31-33 = output -2.
How to do it?
{
"disciplin": [
{
"name": "Playoff",
"teamsize": 2,
"gender": 3,
"system": 32,
"status": 2,
"id": 1
,
"games": [
{
"result": "4:0",
"points": "48:35",
"id": 1
},
{
"result": "4:0",
"points": "31:33",
"id": 2
},
]
,
},
]
}
I tried this ps1 script in PowerShell 7.5.0, but nothing changes in the output file. It's all the same as the original file.
($json = Get-Content -Raw C:\Users\[USER]\Documents\file.json | ConvertFrom-Json)
$json -replace '(?<="points":\s*")(\d+):(\d+)(?=")',
{ [int] $_.Groups[1].Value - $_.Groups[2].Value }
$json | ConvertTo-Json -depth 8 |Out-File "C:\Users\[USER]\Documents\new.json"
The JSON file structure looks like below. I want to change the points value with PowerShell. In id 1, it should be a positive value, 48-35 = output 13, and in id 2, it should be a negative value, 31-33 = output -2.
How to do it?
{
"disciplin": [
{
"name": "Playoff",
"teamsize": 2,
"gender": 3,
"system": 32,
"status": 2,
"id": 1
,
"games": [
{
"result": "4:0",
"points": "48:35",
"id": 1
},
{
"result": "4:0",
"points": "31:33",
"id": 2
},
]
,
},
]
}
I tried this ps1 script in PowerShell 7.5.0, but nothing changes in the output file. It's all the same as the original file.
($json = Get-Content -Raw C:\Users\[USER]\Documents\file.json | ConvertFrom-Json)
$json -replace '(?<="points":\s*")(\d+):(\d+)(?=")',
{ [int] $_.Groups[1].Value - $_.Groups[2].Value }
$json | ConvertTo-Json -depth 8 |Out-File "C:\Users\[USER]\Documents\new.json"
Share
Improve this question
edited Mar 23 at 10:35
Mark Rotteveel
110k229 gold badges156 silver badges224 bronze badges
asked Mar 22 at 21:29
AndersAnders
1137 bronze badges
3
|
1 Answer
Reset to default 0Preface
The solution attempt you later edited into your question is based on a mistaken interpretation of the solutions below, inappropriately mixing an OO-based approach via
ConvertFrom-Json
with a plain-text approach based on string replacements only.While incidental to the question as asked, the bottom section now spells out both approaches in the context of in-place updating of a given JSON file, which seems to be your ultimate goal.
As noted in the answers to your previous question, it is generally better for robustness to use OO processing based on a JSON parser, namely ConvertFrom-Json
, and later reconversion to JSON with ConvertTo-Json
.
In a pinch, you can use plain-text processing with a regex-based string replacement via the -replace
operator even on your entire JSON document, as shown below; alternatively, use the - more verbose but more robust - OO approach and apply the string replacement only to the property values of interest, analogously - see the next section.
Assuming that your JSON document is stored in variable $json
(to read it from a file, use something like $json = Get-Content -Raw file.json
):
- A PowerShell (Core) 7 solution:
$json -replace '(?<="points":\s*)"(\d+):(\d+)"',
{ [int] $_.Groups[1].Value - $_.Groups[2].Value }
- In Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1), direct use of .NET APIs is needed (works in PowerShell 7 too):
[regex]::Replace(
$json,
'(?<="points":\s*)"(\d+):(\d+)"',
{ param($m) [int] $m.Groups[1].Value - $m.Groups[2].Value }
)
Note:
For an explanation of the regex part and the option to experiment with it, see this regex101 page (the linked page uses an equivalent C# verbatim string).
To learn more about the script block-based (
{ ... }
) string-replacement technique, see this answer.The above replaces the string property values (e.g.
"48:35"
) with numeric ones (e.g.13
). If you want the new values to be strings too, use the following regex instead:
'(?<="points":\s*")(\d+):(\d+)(?=")'
In the OO solution below, remove the[int]
cast.
OO solution, for PowerShell (Core) 7 (adaptable to Windows PowerShell analogous to the above):
ConvertFrom-Json $json |
ForEach-Object {
$_.disciplin.games |
ForEach-Object {
$_.points =
[int] $_.points -replace '(\d+):(\d+)',
{ [int] $_.Groups[1].Value - $_.Groups[2].Value }
}
$_ # Output the modified object.
} |
ConvertTo-Json -Depth 4
Note:
-Depth 4
is needed to prevent truncation of the object graph in the to-JSON conversion; if your real JSON is more deeply nested, increase the number as needed - see this post for background information.
Complete, file-based solutions that update the input file in place and work in both PowerShell editions:
- Plain-text solution:
$file = "$HOME\Documents\file.json"
[regex]::Replace(
(Get-Content -Raw $file),
'(?<="points":\s*)"(\d+):(\d+)"',
{ param($m) $m.Groups[1].Value - $m.Groups[2].Value }
) | Set-Content -Encoding utf8 $file
- OO solution:
$file = "$HOME\Documents\file.json"
(Get-Content -Raw $file) |
ConvertFrom-Json |
ForEach-Object {
$_.disciplin.games |
ForEach-Object {
$_.points =
[int] [regex]::Replace(
$_.points,
'(\d+):(\d+)',
{ param($m) [int] $m.Groups[1].Value - $m.Groups[2].Value }
)
}
$_ # Output the modified object.
} |
ConvertTo-Json -Depth 8 |
Set-Content -Encoding utf8 $file
"48:35"
- to a number - e.g.13
, i.e. unquoted; however, if you want to keep the result as string (e.g.,"13"
), you can use the original regex (still mentioned in the updated answer; in the OO approach, remove the outer[int]
cast). – mklement0 Commented Mar 23 at 16:48