Public/Show-AACJson.ps1

function Show-AACJson {
    <#
    .EXTERNALHELP Azure.Admin.Console-help.xml
    .SYNOPSIS
        Shows JSON - or any PowerShell object, as JSON - at the console,
        indented and syntax-coloured in a panel, so an ARM template, a REST
        response or a resource's properties are easy to read.
    .DESCRIPTION
        Pipe in JSON text (an API response, a template file's content) or
        objects (Resource Graph rows, Invoke-RestMethod results, this
        module's output): it's shown indented, with property names, strings,
        numbers, true/false and null each in their own colour:
 
          ╭─ storage account ───────────────────────────────╮
          │ { │
          │ "name": "stcontoso", │
          │ "location": "westeurope", │
          │ "properties": { │
          │ "minimumTlsVersion": "TLS1_2", │
          │ "allowBlobPublicAccess": false │
          │ } │
          │ } │
          ╰─────────────────────────────────────────────────╯
 
        Several objects piped in are shown as one JSON array. JSON text that
        isn't valid is shown as it is, with a warning. Long JSON is paged
        (any key for the next page, A for the rest; -NoPaging turns it off).
        Nothing is sent anywhere: it only formats what it's given.
    .PARAMETER InputObject
        JSON text, or objects to show as JSON.
    .PARAMETER Title
        The panel's title. Defaults to 'JSON'.
    .PARAMETER Depth
        How deep objects are converted to JSON (default 10). Ignored for JSON
        text.
    .PARAMETER PassThru
        Also pass the input on, unchanged.
    .PARAMETER NoPaging
        Show it all at once instead of a page at a time.
    .EXAMPLE
        Get-Content .\azuredeploy.json -Raw | Show-AACJson -Title 'ARM template'
        An ARM template, coloured.
    .EXAMPLE
        Invoke-RestMethod -Uri $uri -Headers $headers | Show-AACJson -Title 'Response'
        A REST response, as JSON.
    .EXAMPLE
        Show-AACDashboard -NoDisplay | Select-Object Status, Headline, Health | Show-AACJson
        Part of the dashboard object, as JSON.
    .OUTPUTS
        The input, with -PassThru.
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory, ValueFromPipeline, Position = 0)]
        [AllowNull()]
        [AllowEmptyString()]
        [object] $InputObject,

        [string] $Title = 'JSON',

        [ValidateRange(1, 100)]
        [int] $Depth = 10,

        [switch] $PassThru,

        [switch] $NoPaging
    )

    begin {
        trap { if ($_.Exception -is [System.Management.Automation.PipelineStoppedException]) { return }; $PSCmdlet.ThrowTerminatingError((Show-AACError -ErrorRecord $_ -Cmdlet $PSCmdlet)) }
        $items = [System.Collections.Generic.List[object]]::new()
    }
    process {
        $items.Add($InputObject)
        if ($PassThru) { $InputObject }
    }
    end {
        trap { if ($_.Exception -is [System.Management.Automation.PipelineStoppedException]) { return }; $PSCmdlet.ThrowTerminatingError((Show-AACError -ErrorRecord $_ -Cmdlet $PSCmdlet)) }
        $allText = $items.Count -and -not @($items | Where-Object { $_ -isnot [string] }).Count
        $json = if ($allText) {
            # JSON text (piped lines are one document): re-indented when it parses.
            $text = ($items -join "`n").Trim()
            try { ConvertFrom-Json -InputObject $text -AsHashtable -Depth 100 -NoEnumerate -ErrorAction Stop | ConvertTo-Json -Depth 100 }
            catch { Write-Warning "That isn't valid JSON, so it's shown as it is: $($_.Exception.Message)"; $text }
        }
        elseif ($items.Count -eq 1) { ConvertTo-Json -InputObject $items[0] -Depth $Depth }
        else { ConvertTo-Json -InputObject $items.ToArray() -Depth $Depth }
        if ($null -eq $json) { $json = 'null' }

        $panel = [Spectre.Console.Panel]::new([Spectre.Console.Markup]::new((ConvertTo-AACJsonMarkup -Json $json)))
        $panel.Border = [Spectre.Console.BoxBorder]::Rounded
        $panel.BorderStyle = [Spectre.Console.Style]::Parse('grey35')
        $panel.Header = [Spectre.Console.PanelHeader]::new(" [bold]$([Spectre.Console.Markup]::Escape($Title))[/] ")
        $panel.Expand = $true
        Invoke-AACPagedOutput -NoPaging:$NoPaging -ScriptBlock { [Spectre.Console.AnsiConsole]::Write($panel) }
    }
}