JSONLab.psm1
|
function Merge-PSObject { <# .SYNOPSIS Create a new PSObject by recursively combining the properties of PSObjects. .INPUTS System.Management.Automation.PSObject to combine. .OUTPUTS System.Management.Automation.PSObject combining the inputs. .FUNCTIONALITY PowerShell .LINK Get-Member .LINK Add-Member .EXAMPLE Merge-PSObject ([pscustomobject]@{a=1;b=2}) ([pscustomobject]@{b=0;c=3}) a b c - - - 1 2 3 .EXAMPLE Merge-PSObject ([pscustomobject]@{a=1;b=2}) ([pscustomobject]@{b=0;c=3}) -Force a b c - - - 1 0 3 .EXAMPLE '{"a":1,"b":{"u":3},"c":{"v":5}}','{"a":{"w":8},"b":2,"c":{"x":6}}' |ConvertFrom-Json |Merge-PSObject -Accumulate -Force |select -Last 1 |ConvertTo-Json { "a": { "w": 8 }, "b": 2, "c": { "v": 5, "x": 6 } } #> [CmdletBinding()][OutputType([PSObject])] Param( # Initial PSObject to combine. [Parameter(Position=0)][PSObject] $ReferenceObject = [pscustomobject]@{}, <# PSObjects to combine. PSObject descendant properties are recursively merged. Primitive values are overwritten by any matching ones in the new PSObject. #> [Parameter(Position=1,Mandatory=$true,ValueFromPipeline=$true)][PSObject] $InputObject, # Continue merging each pipeline object's properties into the same accumulator object. [switch] $Accumulate, # Overwrite existing properties. [switch] $Force ) Begin {if($Accumulate) {$value = $ReferenceObject.PSObject.Copy()}} Process { if(!$Accumulate) {$value = $ReferenceObject.PSObject.Copy()} foreach($p in $InputObject |Get-Member -Type Properties) { $name,$type = $p.Name,$p.MemberType $newvalue = $InputObject.$name if(!($value |Get-Member $name -Type $type)) { $value |Add-Member $name -Type $type -Value $newvalue } elseif($Force) { $currentvalue = $value.$name $value.$name = if($currentvalue -isnot [PSObject] -or $newvalue -isnot [PSObject]) {$newvalue} else {Merge-PSObject $currentvalue $newvalue} } elseif($value.$name -is [PSObject] -and $newvalue -is [PSObject]) { $value.$name = Merge-PSObject $value.$name $newvalue } } return $value } } function Export-Json { <# .SYNOPSIS Exports a portion of a JSON document, recursively importing references. .INPUTS System.String containing JSON, or System.Collections.Hashtable parsed from JSON, or System.Management.Automation.PSObject parsed from JSON. .OUTPUTS System.String containing the extracted JSON. .FUNCTIONALITY Json .LINK http://jsonref.org/ .LINK https://www.rfc-editor.org/rfc/rfc6901 .LINK https://github.com/MicrosoftDocs/PowerShell-Docs/pull/9042 .LINK Select-Json .EXAMPLE '{d:{a:{b:1,c:{"$ref":"#/d/two"}},two:2}}' |Export-Json /d/a { "b": 1, "c": 2 } .EXAMPLE '{d:{a:{b:1,c:{"$ref":"#/d/c"}},c:{d:{"$ref":"#/d/two"}},two:2}}' |Export-Json /d/a -Compress {"b":1,"c":{"d":2}} #> [CmdletBinding()][OutputType([string])] Param( <# The full path name of the property to get, as a JSON Pointer, modified to support wildcards: ~0 = ~ ~1 = / ~2 = ? ~3 = * ~4 = [ #> [Parameter(Position=0)][Alias('Name')][AllowEmptyString()][ValidatePattern('\A(?:|/(?:[^~]|~[0-4])*)\z')] [string] $JsonPointer = '', # The JSON (string or parsed object/hashtable) to get the value from. [Parameter(ParameterSetName='InputObject',ValueFromPipeline=$true)] $InputObject, # A JSON file to update. [Parameter(ParameterSetName='Path',Mandatory=$true)][string] $Path, # Omits white space and indented formatting in the output string. [switch] $Compress ) Begin { if($PSVersionTable.PSVersion -lt 7.3) {Write-Warning "JSON property order may be changed in PowerShell < 7.3"} function Get-Reference { [CmdletBinding()] Param([uri] $ReferenceUri, $Root) $source,$pointer = switch($ReferenceUri) { {$_.OriginalString -like '#*'} {$Root,($_.OriginalString -replace '\A#')} {$_.IsFile} {(Get-Content $_.LocalPath -Raw |ConvertFrom-Json -AsHashtable),($_.Fragment -replace '\A*')} default {(Invoke-RestMethod $ReferenceUri),($_.Fragment -replace '\A#')} } return $source |Select-Json $pointer |Import-Reference -Root $source } filter Import-Reference { [CmdletBinding()] Param($Root, [Parameter(ValueFromPipeline=$true)] $InputObject) if($null -eq $InputObject -or $InputObject -is [bool] -or $InputObject -is [long] -or $InputObject -is [double] -or $InputObject -is [string]) {return $InputObject} if(($InputObject |ConvertTo-Json -Compress -Depth 100) -notlike '*"$ref":*') {return $InputObject} if($InputObject -is [Collections.IList]) {return ,@($InputObject |Import-Reference -Root $Root)} if($InputObject -is [Collections.IDictionary]) { if($InputObject.ContainsKey('$ref')) { return Get-Reference -ReferenceUri $InputObject['$ref'] -Root $Root } foreach($name in @($InputObject.Keys)) { $InputObject[$name] = Import-Reference -Root $Root -InputObject $InputObject[$name] } return $InputObject } if($InputObject.PSObject.Properties.Match('$ref').Count) { return Get-Reference -ReferenceUri ($InputObject.'$ref') -Root $Root } foreach($property in $InputObject.PSObject.Properties) { $name = $property.Name $InputObject.$name = Import-Reference -Root $Root -InputObject ($InputObject.$name) } return $InputObject } if($Path) { return Resolve-Path -Path $Path | ForEach-Object {$_ |Get-Content -Raw | Export-Json -JsonPointer $JsonPointer -Compress:$Compress} } } End { $root = $InputObject -is [string] ? ($InputObject |ConvertFrom-Json -AsHashtable) : $InputObject $selection = $root |Select-Json $JsonPointer return $selection |Import-Reference -Root $root |ConvertTo-Json -Depth 100 -Compress:$Compress } } function Merge-Json { <# .SYNOPSIS Create a new JSON string by recursively combining the properties of JSON strings. .INPUTS System.String of JSON to combine. .OUTPUTS System.String of JSON combining the inputs. .FUNCTIONALITY Json .LINK ConvertFrom-Json .LINK ConvertTo-Json .EXAMPLE '{"a":1,"b":{"u":3},"c":{"v":5}}','{"a":{"w":8},"b":2,"c":{"x":6}}' |Merge-Json { "a": { "w": 8 }, "b": 2, "c": { "v": 5, "x": 6 } } #> [CmdletBinding()][OutputType([string])] Param( <# JSON string to combine. Descendant properties are recursively merged. Primitive values are overwritten by any matching ones in the new JSON string. #> [Parameter(Position=0,Mandatory=$true,ValueFromPipeline=$true,ValueFromRemainingArguments=$true)][string[]] $InputObject, # Omits white space and indented formatting in the output string. [switch]$Compress ) Begin {$value = [pscustomobject]@{}} #TODO: Add or replace dependency. Process {$value = $value,($InputObject |ConvertFrom-Json) |Merge-PSObject} End {$value |ConvertTo-Json -Compress:$Compress} } function Resolve-JsonPointer { <# .SYNOPSIS Returns matching JSON Pointer paths, given a JSON Pointer path with wildcards. .INPUTS System.String containing JSON, or System.Collections.Hashtable parsed from JSON, or System.Management.Automation.PSObject parsed from JSON. .OUTPUTS System.String of the full JSON Pointer matched. .FUNCTIONALITY Json .LINK https://www.rfc-editor.org/rfc/rfc6901 .LINK ConvertFrom-Json .EXAMPLE '{a:1}' |Resolve-JsonPointer /* /a .EXAMPLE '[8, 7, 6]' |Resolve-JsonPointer /- /3 .EXAMPLE Resolve-JsonPointer /powershell.*.preset -Path ./.vscode/settings.json /powershell.codeFormatting.preset .EXAMPLE '{"a":1, "b": {"ZZ/ZZ": {"AD~BC": 7}}}' |Resolve-JsonPointer /*/ZZ?ZZ/AD?BC /b/ZZ~1ZZ/AD~0BC .EXAMPLE '{"a":1, "b": {"ZZ/ZZ": {"AD~BC": 7}}}' |Resolve-JsonPointer /[bc]/ZZ?ZZ /b/ZZ~1ZZ .EXAMPLE '{"a":1, "b": {"ZZ/ZZ": {"AD~BC": [7, 8, 9]}}}' |ConvertFrom-Json |Resolve-JsonPointer /?/ZZ*/*BC/- /b/ZZ~1ZZ/AD~0BC/3 .EXAMPLE Resolve-JsonPointer /* -Path .\test\data\sample-openapi.json -IncludePath Path Pointer ---- ------- A:\Scripts\test\data\sample-openapi.json /openapi A:\Scripts\test\data\sample-openapi.json /info A:\Scripts\test\data\sample-openapi.json /tags A:\Scripts\test\data\sample-openapi.json /paths A:\Scripts\test\data\sample-openapi.json /components #> [CmdletBinding()][OutputType([string])] Param( <# The full path name of the property to get, as a JSON Pointer, modified to support wildcards: ~0 = ~ ~1 = / ~2 = ? ~3 = * ~4 = [ #> [Parameter(Position=0)][Alias('Name')][AllowEmptyString()][ValidatePattern('\A(?:|/(?:[^~]|~[0-4])*)\z')] [string] $JsonPointer = '', # The JSON (string or parsed object/hashtable) to get the value from. [Parameter(ParameterSetName='InputObject',ValueFromPipeline=$true)] $InputObject, # A JSON file to update. [Parameter(ParameterSetName='Path',Mandatory=$true)][string] $Path, # Indicates that the source file path should be included in the output, if available. [switch] $IncludePath ) Begin { [string[]] $jsonpath = switch($JsonPointer) { '' {,@()} default {,@($_ -replace '\A/' -replace '~4','[[]' -replace '~3','[*]' -replace '~2','[?]' -split '/' -replace '~1','/' -replace '~0','~')} } function Resolve-Segment { [CmdletBinding()] Param( [Parameter(Position=0)] $InputObject, [Parameter(Position=1)][string] $Segment, [Parameter(Position=2)][string[]] $Segments, [Parameter(Position=3)][string] $JsonPointer ) $pointer = "$JsonPointer/$($segment -replace '~','~0' -replace '/','~1')" if($InputObject -is [array]) { if($Segment -eq '-') {return Resolve-Pointer -InputObject $null -Segments $Segments -JsonPointer "$JsonPointer/$($InputObject.Count)"} elseif(![int]::TryParse($Segment,[ref]$Segment)) {return} elseif($InputObject.Length -le $Segment) {return} else {return Resolve-Pointer -InputObject ($InputObject[$Segment]) -Segments $Segments -JsonPointer $pointer} } elseif($InputObject -is [Collections.IDictionary]) { if(!$InputObject.ContainsKey($Segment)) {return} else {return Resolve-Pointer -InputObject ($InputObject[$Segment]) -Segments $Segments -JsonPointer $pointer} } else { return $InputObject.PSObject.Properties.Match($Segment) | Select-Object -ExpandProperty Value | Resolve-Pointer -Segments $Segments -JsonPointer $pointer } } function Resolve-Wildcard { [CmdletBinding()] Param( [Parameter(Position=0)] $InputObject, [Parameter(Position=1)][string] $Segment, [Parameter(Position=2)][string[]] $Segments, [Parameter(Position=3)][string] $JsonPointer ) if($InputObject -is [array]) { return 0..($InputObject.Length-1) -like $segment |ForEach-Object { $pointer = "$JsonPointer/$_" Resolve-Pointer -InputObject $InputObject[$_] -Segments $Segments -JsonPointer $pointer} } elseif($InputObject -is [Collections.IDictionary]) { return $InputObject.Keys -like $segment |ForEach-Object { $pointer = "$JsonPointer/$($_ -replace '~','~0' -replace '/','~1')" Resolve-Pointer -InputObject $InputObject[$_] -Segments $Segments -JsonPointer $pointer} } else { return $InputObject.PSObject.Properties.Match($segment) |ForEach-Object { $pointer = "$JsonPointer/$($_.Name -replace '~','~0' -replace '/','~1')" Resolve-Pointer -InputObject $_.Value -Segments $Segments -JsonPointer $pointer} } } filter Resolve-Pointer { [CmdletBinding()] Param( [Parameter(ValueFromPipeline=$true)] $InputObject, [string[]] $Segments, [string] $JsonPointer = '' ) if(!$Segments.Count) {return $JsonPointer} $segment,$Segments = $Segments if($null -eq $Segments) {[string[]]$Segments = @()} if($segment -match '[?*[]') {Resolve-Wildcard -InputObject $InputObject -Segment $segment -Segments $Segments -JsonPointer $JsonPointer} else {Resolve-Segment -InputObject $InputObject -Segment $segment -Segments $Segments -JsonPointer $JsonPointer} } } Process { if($Path) { if($IncludePath) { return Resolve-Path -Path $Path -PipelineVariable file | Get-Content -Raw | ForEach-Object {Resolve-Pointer -InputObject ($_ |ConvertFrom-Json -AsHashtable) -Segments $jsonpath} | ForEach-Object {[pscustomobject]@{Path=$file.Path; Pointer=$_}} } else { return Resolve-Path -Path $Path | Get-Content -Raw | ForEach-Object {Resolve-Pointer -InputObject ($_ |ConvertFrom-Json -AsHashtable) -Segments $jsonpath} } } if($null -eq $InputObject) {return} if($InputObject -is [string]) { return Resolve-Pointer -InputObject ($InputObject |ConvertFrom-Json -AsHashtable) -Segments $jsonpath } if(!$jsonpath.Length) {return $JsonPointer} return Resolve-Pointer -InputObject $InputObject -Segments $jsonpath } } function Select-Json { <# .SYNOPSIS Returns a value from a JSON string or file. .INPUTS System.String containing JSON, or System.Collections.Hashtable parsed from JSON, or System.Management.Automation.PSObject parsed from JSON. .OUTPUTS System.Boolean, System.Int64, System.Double, System.String, System.Management.Automation.PSObject, or System.Management.Automation.OrderedHashtable (or null) selected from JSON. .FUNCTIONALITY Json .LINK https://www.rfc-editor.org/rfc/rfc6901 .LINK ConvertFrom-Json .EXAMPLE 'true' |Select-Json # default selection is entire parsed JSON document True .EXAMPLE '{"":3.14}' |Select-Json / 3.14 .EXAMPLE Select-Json /powershell.codeFormatting.preset -Path ./.vscode/settings.json Allman .EXAMPLE '{"a":1, "b": {"ZZ/ZZ": {"AD~BC": 7}}}' |Select-Json /b/ZZ~1ZZ Name Value ---- ----- AD~BC 7 .EXAMPLE '{"a":1, "b": {"ZZ/ZZ": {"AD~BC": 7}}}' |ConvertFrom-Json |Select-Json /b/ZZ~1ZZ AD~BC ----- 7 .EXAMPLE '{"a":1, "b": {"ZZ/ZZ": {"AD~BC": 7}}}' |Select-Json /b/ZZ~1ZZ |ConvertTo-Json -Compress {"AD~BC":7} .EXAMPLE '{d:{a:{b:1,c:{"$ref":"#/d/c"}},c:{d:{"$ref":"#/d/two"}},two:2}}' |Select-Json /*/a/c/* -FollowReferences 2 .EXAMPLE Resolve-Path $env:LOCALAPPDATA/Packages/*WindowsTerminal*/LocalState/settings.json |Get-Item |Get-Content -Raw |Select-Json /profiles/list/*/name PowerShell Windows PowerShell Ubuntu F# Interactive F# REPL C# REPL Command Prompt Media Server (ssh) Azure Cloud Shell Developer Command Prompt for VS 2022 Developer PowerShell for VS 2022 #> [CmdletBinding()][OutputType([bool],[long],[double],[string],[Management.Automation.OrderedHashtable])] Param( <# The full path name of the property to get, as a JSON Pointer, modified to support wildcards: ~0 = ~ ~1 = / ~2 = ? ~3 = * ~4 = [ #> [Parameter(Position=0)][Alias('Name')][AllowEmptyString()][ValidatePattern('\A(?:|/(?:[^~]|~[0-4])*)\z')] [string] $JsonPointer = '', # The JSON (string or parsed object/hashtable) to get the value from. [Parameter(ParameterSetName='InputObject',ValueFromPipeline=$true)] $InputObject, # A JSON file to update. [Parameter(ParameterSetName='Path',Mandatory=$true)][string] $Path, # Indicates that references should be followed. [Alias('FollowRefs','References')][switch] $FollowReferences ) Begin { [string[]] $jsonpath = switch($JsonPointer) { '' {,@()} default {,@($_ -replace '\A/' -replace '~4','[[]' -replace '~3','[*]' -replace '~2','[?]' -split '/' -replace '~1','/' -replace '~0','~')} } function Get-ReferenceUri { [CmdletBinding()] Param($InputObject) if($InputObject -is [array]) {return} if($InputObject -is [Collections.IDictionary] -and $InputObject.ContainsKey('$ref')) {return $InputObject['$ref']} if($InputObject.PSObject.Properties.Match('$ref').Count) {return $InputObject.'$ref'} } function Get-Reference { [CmdletBinding()] Param([uri] $ReferenceUri, $Root) $source,$pointer = switch($ReferenceUri) { {$null -eq $_} {$Root,''; continue} {$_.OriginalString -like '#*'} {$Root,($_.OriginalString -replace '\A#')} {$_.IsFile} {(Get-Content $_.LocalPath -Raw |ConvertFrom-Json -AsHashtable),($_.Fragment -replace '\A*')} default {(Invoke-RestMethod $ReferenceUri),($_.Fragment -replace '\A#')} } return $source |Select-Json $pointer } function Select-Segment { [CmdletBinding()] Param([Parameter(Position=0)] $InputObject, [Parameter(Position=1)][string] $Segment) if($InputObject -is [array]) { if(![int]::TryParse($Segment,[ref]$Segment)) {return} elseif($InputObject.Length -le $Segment) {return} else {return,$InputObject[$Segment]} } elseif($InputObject -is [Collections.IDictionary]) { if(!$InputObject.ContainsKey($Segment)) {return} else {return,$InputObject[$Segment]} } else { return,($InputObject.PSObject.Properties.Match($Segment) | Select-Object -ExpandProperty Value) } } function Select-Wildcard { [CmdletBinding()] Param([Parameter(Position=0)] $InputObject, [Parameter(Position=1)][string] $Segment) if($InputObject -is [array]) { return 0..($InputObject.Length-1) -like $Segment |ForEach-Object {$InputObject[$_]} } elseif($InputObject -is [Collections.IDictionary]) { return $InputObject.Keys -like $Segment |ForEach-Object {$InputObject[$_]} } else { return $InputObject.PSObject.Properties.Match($Segment) | Select-Object -ExpandProperty Value } } filter Select-Pointer { [CmdletBinding()] Param( [Parameter(ValueFromPipeline=$true)] $InputObject, [string[]] $Segments ) if($FollowReferences) { $refUri = Get-ReferenceUri $InputObject if($refUri) {return Get-Reference -ReferenceUri $refUri -Root $Script:Root |Select-Pointer -Segments $Segments} } if(!$Segments.Count) {return $InputObject} $segment,$tail = $Segments if($null -eq $tail) {[string[]]$tail = @()} if($segment -match '[?*[]') {Select-Wildcard $InputObject $segment |Select-Pointer -Segments $tail} else {Select-Segment $InputObject $segment |Select-Pointer -Segments $tail} } } Process { if($Path) { return Resolve-Path -Path $Path | Get-Content -Raw | ForEach-Object {$_ |ConvertFrom-Json -AsHashtable | Select-Json -JsonPointer $JsonPointer -FollowReferences:$FollowReferences} } if($null -eq $InputObject) {return} if($InputObject -is [string]) { if($InputObject.StartsWith(([char]0xFEFF))) {$InputObject = $InputObject.Substring(1)} return Select-Json -JsonPointer $JsonPointer -FollowReferences:$FollowReferences ` -InputObject ($InputObject |ConvertFrom-Json -AsHashtable -NoEnumerate) } if(!$jsonpath.Length) {return $InputObject} $Script:Root = $InputObject return Select-Pointer -InputObject $InputObject -Segments $jsonpath } } function Set-Json { <# .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 -PropertyValue $true true .EXAMPLE '{}' |Set-Json / $false { "": false } .EXAMPLE '{}' |Set-Json /~1/~0 3.14 { "/": { "~": 3.14 } } .EXAMPLE '[1, 2, 3]' |Set-Json /1 0 [ 1, 0, 3 ] .EXAMPLE '[1, 2]' |Set-Json /- 3 [ 1, 2, 3 ] .EXAMPLE '{a:{b:[1,2]}}' |Set-Json /a/b/- 3 { "a": { "b": [ 1, 2, 3 ] } } .EXAMPLE '{a:1}' |Set-Json /b/ZZ~1ZZ/AD~0BC 7 { "a": 1, "b": { "ZZ/ZZ": { "AD~BC": 7 } } } .EXAMPLE Set-Json /powershell.codeFormatting.preset Allman -Path ./.vscode/settings.json Sets "powershell.codeFormatting.preset": "Allman" within the ./.vscode/settings.json file. #> [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)) {throw "Could not use array index $segment"} 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)) {throw "Could not use array index $segment"} 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} } } Export-ModuleMember -Function Export-Json,Merge-Json,Resolve-JsonPointer,Select-Json,Set-Json |