-
Notifications
You must be signed in to change notification settings - Fork 29
/
Set-Json.ps1
167 lines (143 loc) · 4.02 KB
/
Set-Json.ps1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
<#
.SYNOPSIS
Sets a property in a JSON string or file.
.INPUTS
System.String containing JSON.
.OUTPUTS
System.String containing updated JSON (unless a file is specified, which is updated).
.FUNCTIONALITY
Json
.LINK
https://www.rfc-editor.org/rfc/rfc6901
.LINK
ConvertFrom-Json
.LINK
ConvertTo-Json
.LINK
Add-Member
.EXAMPLE
'0' |Set-Json.ps1 -PropertyValue $true
true
.EXAMPLE
'{}' |Set-Json.ps1 / $false
{
"": false
}
.EXAMPLE
'{}' |Set-Json.ps1 /~1/~0 3.14
{
"/": {
"~": 3.14
}
}
.EXAMPLE
'[1, 2, 3]' |Set-Json.ps1 /1 0
[
1,
0,
3
]
.EXAMPLE
'[1, 2]' |Set-Json.ps1 /- 3
[
1,
2,
3
]
.EXAMPLE
'{a:{b:[1,2]}}' |Set-Json.ps1 /a/b/- 3
{
"a": {
"b": [
1,
2,
3
]
}
}
.EXAMPLE
'{a:1}' |Set-Json.ps1 /b/ZZ~1ZZ/AD~0BC 7
{
"a": 1,
"b": {
"ZZ/ZZ": {
"AD~BC": 7
}
}
}
.EXAMPLE
Set-Json.ps1 /powershell.codeFormatting.preset Allman -Path ./.vscode/settings.json
Sets "powershell.codeFormatting.preset": "Allman" within the ./.vscode/settings.json file.
#>
#Requires -Version 7
[CmdletBinding()][OutputType([string])] Param(
<#
The full path name of the property to set, as a JSON Pointer, which separates each nested
element name with a /, and literal / is escaped as ~1, and literal ~ is escaped as ~0.
#>
[Parameter(Position=0)][Alias('Name')][AllowEmptyString()][ValidatePattern('\A(?:|/(?:[^~]|~0|~1)*)\z')]
[string] $JsonPointer = '',
# The value to set the property to.
[Parameter(Position=1,Mandatory=$true)][AllowEmptyString()][AllowEmptyCollection()][AllowNull()]
[Alias('Value')][psobject] $PropertyValue,
# Indicates that overwriting values should generate a warning.
[switch] $WarnOverwrite,
# The JSON string to set the property in.
[Parameter(ParameterSetName='InputObject',Mandatory=$true,ValueFromPipeline=$true)][string] $InputObject,
# A JSON file to update.
[Parameter(ParameterSetName='Path',Mandatory=$true)][string] $Path
)
Begin
{
[string[]] $jsonpath = switch($JsonPointer) { '' {,@()}
default {,@($_ -replace '\A/' -split '/' -replace '~1','/' -replace '~0','~')} }
}
Process
{
if(!$jsonpath.Length)
{
if($Path) {$PropertyValue |ConvertTo-Json -Depth 100 |Out-File $Path utf8NoBOM; return}
else {return $PropertyValue |ConvertTo-Json -Depth 100}
}
$object = ($Path ? (Get-Content $Path -Raw) : $InputObject) |ConvertFrom-Json -AsHashtable
if($null -eq $object) {return}
$property,$parent = $object,$null
for($i = 0; $i -lt ($jsonpath.Length-1); $i++)
{
$segment = $jsonpath[$i]
if($property -is [array])
{
if($i -eq 0 -and $segment -eq '-') {$property = @{}; $object += $property; $segment = $object.Count}
if(![int]::TryParse($segment,[ref]$segment)) {Stop-ThrowError.ps1 "Could not use array index $segment" -Argument JsonPointer}
elseif($property.Count -le $segment) {$property = @{}; $object += $property; $segment = $object.Count}
else {$property,$parent = $property[$segment],$property}
}
else
{
if(!$property.ContainsKey($segment)) {$property[$segment] = @{}}
$property,$parent = $property.$segment,$property
}
if($property -is [array] -and $i -lt ($jsonpath.Length-2) -and $jsonpath[$i+1] -eq '-')
{ # RFC6091 uses '-' to append to an array
$property += @{}
$jsonpath[$i+1] = $property.Count
$parent.$segment = $property
}
}
$segment = $jsonpath[-1]
if($property -is [array])
{
if($segment -eq '-') {if($jsonpath.Length -eq 1) {$object += $PropertyValue} else {$parent.$($jsonpath[-2]) += $PropertyValue}}
elseif(![int]::TryParse($segment,[ref]$segment)) {Stop-ThrowError.ps1 "Could not use array index $segment" -Argument JsonPointer}
elseif($property.Count -le $segment) {if($jsonpath.Length -eq 1) {$object += $PropertyValue} else {$parent.$($jsonpath[-2]) += $PropertyValue}}
else {$property[$segment] = $PropertyValue}
}
else
{
if($property.ContainsKey($segment) -and $WarnOverwrite) {Write-Warning "Property $JsonPointer overwriting '$($property.$segment)'."}
$property[$segment] = $PropertyValue
}
$value = $object |ConvertTo-Json -Depth 100
if($Path) {$value |Out-File $Path utf8NoBOM}
else {return $value}
}