Files
Scoop/lib/json.ps1

213 lines
6.5 KiB
PowerShell

# Convert objects to pretty json
# Only needed until PowerShell ConvertTo-Json will be improved https://github.com/PowerShell/PowerShell/issues/2736
# https://github.com/PowerShell/PowerShell/issues/2736 was fixed in pwsh
# Still needed in normal powershell
function ConvertToPrettyJson {
[CmdletBinding()]
Param (
[Parameter(Mandatory, ValueFromPipeline)]
$data
)
Process {
$data = normalize_values $data
# convert to string
[String]$json = $data | ConvertTo-Json -Depth 8 -Compress
[String]$output = ''
# state
[String]$buffer = ''
[Int]$depth = 0
[Bool]$inString = $false
# configuration
[String]$indent = ' ' * 4
[Bool]$unescapeString = $true
[String]$eol = "`r`n"
for ($i = 0; $i -lt $json.Length; $i++) {
# read current char
$buffer = $json.Substring($i, 1)
$objectStart = !$inString -and $buffer.Equals('{')
$objectEnd = !$inString -and $buffer.Equals('}')
$arrayStart = !$inString -and $buffer.Equals('[')
$arrayEnd = !$inString -and $buffer.Equals(']')
$colon = !$inString -and $buffer.Equals(':')
$comma = !$inString -and $buffer.Equals(',')
$quote = $buffer.Equals('"')
$escape = $buffer.Equals('\')
if ($quote) {
$inString = !$inString
}
# skip escape sequences
if ($escape) {
$buffer = $json.Substring($i, 2)
++$i
# Unescape unicode
if ($inString -and $unescapeString) {
if ($buffer.Equals('\n')) {
$buffer = "`n"
} elseif ($buffer.Equals('\r')) {
$buffer = "`r"
} elseif ($buffer.Equals('\t')) {
$buffer = "`t"
} elseif ($buffer.Equals('\u')) {
$buffer = [regex]::Unescape($json.Substring($i - 1, 6))
$i += 4
}
}
$output += $buffer
continue
}
# indent / outdent
if ($objectStart -or $arrayStart) {
++$depth
} elseif ($objectEnd -or $arrayEnd) {
--$depth
$output += $eol + ($indent * $depth)
}
# add content
$output += $buffer
# add whitespace and newlines after the content
if ($colon) {
$output += ' '
} elseif ($comma -or $arrayStart -or $objectStart) {
$output += $eol
$output += $indent * $depth
}
}
return $output
}
}
function json_path([String] $json, [String] $jsonpath, [Hashtable] $substitutions, [Boolean] $reverse, [Boolean] $single) {
Add-Type -Path "$PSScriptRoot\..\supporting\validator\bin\Newtonsoft.Json.dll"
if ($null -ne $substitutions) {
$jsonpath = substitute $jsonpath $substitutions ($jsonpath -like "*=~*")
}
try {
$obj = [Newtonsoft.Json.Linq.JValue]::Parse($json)
} catch [Newtonsoft.Json.JsonReaderException] {
return $null
}
try {
$result = $obj.SelectTokens($jsonpath, $true)
if ($reverse) {
# Return versions in reverse order
$result = [System.Linq.Enumerable]::Reverse($result)
}
if ($single) {
# Extract First value
$result = [System.Linq.Enumerable]::First($result)
# Convert first value to string
$result = $result.ToString()
} else {
$result = "$([String]::Join('\n', $result))"
}
return $result
} catch [Exception] {
Write-Host $_ -ForegroundColor DarkRed
}
return $null
}
function json_path_legacy([String] $json, [String] $jsonpath, [Hashtable] $substitutions) {
$result = $json | ConvertFrom-Json -ea stop
$isJsonPath = $jsonpath.StartsWith('$')
$jsonpath.split('.') | ForEach-Object {
$el = $_
# substitute the basename and version varibales into the jsonpath
if ($null -ne $substitutions) {
$el = substitute $el $substitutions
}
# skip $ if it's jsonpath format
if ($el -eq '$' -and $isJsonPath) {
return
}
# array detection
if ($el -match '^(?<property>\w+)?\[(?<index>\d+)\]$') {
$property = $matches['property']
if ($property) {
$result = $result.$property[$matches['index']]
} else {
$result = $result[$matches['index']]
}
return
}
$result = $result.$el
}
return $result
}
function normalize_values([psobject] $json) {
# Iterate Through Manifest Properties
$json.PSObject.Properties | ForEach-Object {
# Recursively edit psobjects
# If the values is psobjects, its not normalized
# For example if manifest have architecture and it's architecture have array with single value it's not formatted.
# @see https://github.com/ScoopInstaller/Scoop/pull/2642#issue-220506263
if ($_.Value -is [System.Management.Automation.PSCustomObject]) {
$_.Value = normalize_values $_.Value
}
# Process String Values
if ($_.Value -is [String]) {
# Split on new lines
[Array] $parts = ($_.Value -split '\r?\n').Trim()
# Replace with string array if result is multiple lines
if ($parts.Count -gt 1) {
$_.Value = $parts
}
}
# Convert single value array into string
if ($_.Value -is [Array]) {
# Array contains only 1 element String or Array
if ($_.Value.Count -eq 1) {
# Array
if ($_.Value[0] -is [Array]) {
$_.Value = $_.Value
} else {
# String
$_.Value = $_.Value[0]
}
} else {
# Array of Arrays
$resulted_arrs = @()
foreach ($element in $_.Value) {
if ($element.Count -eq 1) {
$resulted_arrs += $element
} else {
$resulted_arrs += , $element
}
}
$_.Value = $resulted_arrs
}
}
# Process other values as needed...
}
return $json
}