使用powershell正确格式化JSON

3okqufwl  于 11个月前  发布在  Shell
关注(0)|答案(6)|浏览(250)

下面是一个JSON格式的文件:
ConvertTo-JSON之前:

[
    {
        "Yura": {
            "Cashier": {
                "branch": "release/Retail-v4.0",
                "configuration": "RetailDemo Debug",
                "datetime_deployed": "Apr 18 2018 07:45:05",
                "deployed_by": "anonymous",
                "host": "cashier2-retail4.testing.aws.com",
                "job": "http://jenkins-testing.aws.com:8080/job/CashierDeployment",
                "lineserver": "",
                "messagebus": "",
                "product": "Cashier",
                "publish_profile": "cashier2.retail.dev.pubxml"
            },
            "ContentManager": {
                "branch": "release/Retail-v3.31.1",
                "configuration": "RetailDemo Debug",
                "datetime_deployed": "Jan 17 2018 11:59:24",
                "deployed_by": "anonymous",
                "host": "contentmanager2-retail3.testing.aws.com",
                "job": "http://jenkins-testing.aws.com:8080/job/ContentManagerDeployment",
                "lineserver": "",
                "messagebus": "",
                "product": "ContentManager",
                "publish_profile": "..\\ContentManager.PublishProfiles\\contentmanager2.retail5.dev.pubxml"
            }
        }
    }
]

字符串
在使用此代码处理数据之后:

$json = Get-Content 'D:\script\test.json'  -encoding utf8 | ConvertFrom-Json
$json.yura.ContentManager.branch = 'test'


我将JSON保存到另一个文件中:

$json | convertto-json | set-content "D:\script\test1.json" -encoding utf8


问题是,保存文件后,格式变得破碎:

{
    "Yura":  {
                 "Cashier":  {
                                 "branch":  "release/Retail-v4.0",
                                 "configuration":  "RetailDemo Debug",
                                 "datetime_deployed":  "Apr 18 2018 07:45:05",
                                 "deployed_by":  "anonymous",
                                 "host":  "cashier2-retail4.testing.aws.com",
                                 "job":  "http://jenkins-testing.aws.com:8080/job/CashierDeployment",
                                 "lineserver":  "",
                                 "messagebus":  "",
                                 "product":  "Cashier",
                                 "publish_profile":  "cashier2.retail.dev.pubxml"
                             },
                 "ContentManager":  {
                                        "branch":  "test",
                                        "configuration":  "RetailDemo Debug",
                                        "datetime_deployed":  "Jan 17 2018 11:59:24",
                                        "deployed_by":  "anonymous",
                                        "host":  "contentmanager2-retail3.testing.aws.com",
                                        "job":  "http://jenkins-testing.aws.com:8080/job/ContentManagerDeployment",
                                        "lineserver":  "",
                                        "messagebus":  "",
                                        "product":  "ContentManager",
                                        "publish_profile":  "..\\ContentManager.PublishProfiles\\contentmanager2.retail5.dev.pubxml"
                                    }
             }
}


我的问题是-如何在PowerShell中保留源代码格式?

gwbalxhn

gwbalxhn1#

由于您的原始json包含一个只有一个元素的数组,因此PowerShell会将其压缩为这一个元素。如果您希望在输出中再次将其作为数组,请使用rokumaru's good answer
然而,PowerShell的ConvertTo-Json并不能生成格式很好的json,为此,我在前一段时间写了一个帮助函数:

已编辑

新版本(感谢Widlov

function Format-Json {
    <#
    .SYNOPSIS
        Prettifies JSON output.
        Version January 3rd 2024
        Fixes:
            - empty [] or {} or in-line arrays as per https://stackoverflow.com/a/71664664/9898643
              by Widlov (https://stackoverflow.com/users/1716283/widlov)
            - Unicode Apostrophs \u0027 as written by ConvertTo-Json are replaced with regular single quotes "'"
            - multiline empty [] or {} are converted into inline arrays or objects
    .DESCRIPTION
        Reformats a JSON string so the output looks better than what ConvertTo-Json outputs.
    .PARAMETER Json
        Required: [string] The JSON text to prettify.
    .PARAMETER Minify
        Optional: Returns the json string compressed.
    .PARAMETER Indentation
        Optional: The number of spaces (1..1024) to use for indentation. Defaults to 2.
    .PARAMETER AsArray
        Optional: If set, the output will be in the form of a string array, otherwise a single string is output.
    .EXAMPLE
        $json | ConvertTo-Json | Format-Json -Indentation 4
    .OUTPUTS
        System.String or System.String[] (the latter when parameter AsArray is set)
    #>
    [CmdletBinding(DefaultParameterSetName = 'Prettify')]
    Param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [string]$Json,

        [Parameter(ParameterSetName = 'Minify')]
        [switch]$Minify,

        [Parameter(ParameterSetName = 'Prettify')]
        [ValidateRange(1, 1024)]
        [int]$Indentation = 2,

        [Parameter(ParameterSetName = 'Prettify')]
        [switch]$AsArray
    )

    if ($PSCmdlet.ParameterSetName -eq 'Minify') {
        return ($Json | ConvertFrom-Json) | ConvertTo-Json -Depth 100 -Compress
    }

    # If the input JSON text has been created with ConvertTo-Json -Compress
    # then we first need to reconvert it without compression
    if ($Json -notmatch '\r?\n') {
        $Json = ($Json | ConvertFrom-Json) | ConvertTo-Json -Depth 100
    }

    $indent = 0
    $regexUnlessQuoted = '(?=([^"]*"[^"]*")*[^"]*$)'

    $result = ($Json -split '\r?\n' | ForEach-Object {
        # If the line contains a ] or } character, 
        # we need to decrement the indentation level unless:
        #   - it is inside quotes, AND
        #   - it does not contain a [ or {
        if (($_ -match "[}\]]$regexUnlessQuoted") -and ($_ -notmatch "[\{\[]$regexUnlessQuoted")) {
            $indent = [Math]::Max($indent - $Indentation, 0)
        }

        # Replace all colon-space combinations by ": " unless it is inside quotes.
        $line = (' ' * $indent) + ($_.TrimStart() -replace ":\s+$regexUnlessQuoted", ': ')

        # If the line contains a [ or { character, 
        # we need to increment the indentation level unless:
        #   - it is inside quotes, AND
        #   - it does not contain a ] or }
        if (($_ -match "[\{\[]$regexUnlessQuoted") -and ($_ -notmatch "[}\]]$regexUnlessQuoted")) {
            $indent += $Indentation
        }

        # ConvertTo-Json returns all single-quote characters as Unicode Apostrophs \u0027
        # see: https://stackoverflow.com/a/29312389/9898643
        $line -replace '\\u0027', "'"

    # join the array with newlines and convert multiline empty [] or {} into inline arrays or objects
    }) -join [Environment]::NewLine -replace '(\[)\s+(\])', '$1$2' -replace '(\{)\s+(\})', '$1$2'

    if ($AsArray) { return ,[string[]]($result -split '\r?\n') }
    $result
}

字符串
像这样使用它:

$json = Get-Content 'D:\script\test.json' -Encoding UTF8 | ConvertFrom-Json
$json.yura.ContentManager.branch = 'test'

# recreate the object as array, and use the -Depth parameter (your json needs 3 minimum)
ConvertTo-Json @($json) -Depth 3 | Format-Json | Set-Content "D:\script\test1.json" -Encoding UTF8

# instead of using '@($json)' you can of course also recreate the array by adding the square brackets manually:
# '[{0}{1}{0}]' -f [Environment]::NewLine, ($json | ConvertTo-Json -Depth 3) | 
#        Format-Json | Set-Content "D:\script\test1.json" -Encoding UTF8

gdx19jrr

gdx19jrr2#

我发现Theo's excellent answer导致空[]或{}或行内数组的缩进错误。
输入示例:

$JsonA = '{
    "Henk": "test",
    "Piet": [],
    "blub": {},
    "Joop": [
        {
            "a": "aaa",
            "b": [],
            "z": "lsjkal"
        }
    ],
    "Klaas": [
        [[],["a"],["b","c"]],
        ["a","b"],
        ["c"],
        [],
        "henk"
    ]
}'

$JsonA | Format-Json

字符串
将导致:

{
    "Henk": "test",
"Piet": [],
"blub": {},
    "Joop": [
        {
            "a": "aaa",
        "b": [],
            "z": "lsjkal"
        }
    ],
    "Klaas": [
    [[],["a"],["b","c"]],
    ["a","b"],
    ["c"],
    [],
        "henk"
    ],
}


写了一个修复程序,以确保当行包含[/{ AND ]/}时缩进正确:

function Format-Json {
    <#
    .SYNOPSIS
        Prettifies JSON output.
    .DESCRIPTION
        Reformats a JSON string so the output looks better than what ConvertTo-Json outputs.
    .PARAMETER Json
        Required: [string] The JSON text to prettify.
    .PARAMETER Minify
        Optional: Returns the json string compressed.
    .PARAMETER Indentation
        Optional: The number of spaces (1..1024) to use for indentation. Defaults to 4.
    .PARAMETER AsArray
        Optional: If set, the output will be in the form of a string array, otherwise a single string is output.
    .EXAMPLE
        $json | ConvertTo-Json  | Format-Json -Indentation 2
    #>
    [CmdletBinding(DefaultParameterSetName = 'Prettify')]
    Param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [string]$Json,

        [Parameter(ParameterSetName = 'Minify')]
        [switch]$Minify,

        [Parameter(ParameterSetName = 'Prettify')]
        [ValidateRange(1, 1024)]
        [int]$Indentation = 4,

        [Parameter(ParameterSetName = 'Prettify')]
        [switch]$AsArray
    )

    if ($PSCmdlet.ParameterSetName -eq 'Minify') {
        return ($Json | ConvertFrom-Json) | ConvertTo-Json -Depth 100 -Compress
    }

    # If the input JSON text has been created with ConvertTo-Json -Compress
    # then we first need to reconvert it without compression
    if ($Json -notmatch '\r?\n') {
        $Json = ($Json | ConvertFrom-Json) | ConvertTo-Json -Depth 100
    }

    $indent = 0
    $regexUnlessQuoted = '(?=([^"]*"[^"]*")*[^"]*$)'

    $result = $Json -split '\r?\n' |
        ForEach-Object {
            # If the line contains a ] or } character, 
            # we need to decrement the indentation level, unless:
            #   - it is inside quotes, AND
            #   - it does not contain a [ or {
            if (($_ -match "[}\]]$regexUnlessQuoted") -and ($_ -notmatch "[\{\[]$regexUnlessQuoted")) {
                $indent = [Math]::Max($indent - $Indentation, 0)
            }

            # Replace all colon-space combinations by ": " unless it is inside quotes.
            $line = (' ' * $indent) + ($_.TrimStart() -replace ":\s+$regexUnlessQuoted", ': ')

            # If the line contains a [ or { character, 
            # we need to increment the indentation level, unless:
            #   - it is inside quotes, AND
            #   - it does not contain a ] or }
            if (($_ -match "[\{\[]$regexUnlessQuoted") -and ($_ -notmatch "[}\]]$regexUnlessQuoted")) {
                $indent += $Indentation
            }

            $line
        }

    if ($AsArray) { return $result }
    return $result -Join [Environment]::NewLine
}

mum43rcc

mum43rcc3#

如果整体是一个数组,而元素是一个单独的json文件,那就很尴尬了。
如果使用管道,则不会将其视为数组。

$json | ConvertTo-Json -Depth 10 # bad

字符串
因为它不是一个普通的数组,所以仅仅把它作为参数传递是行不通的。

ConvertTo-Json $json -Depth 10  # bad


如果您重新创建阵列,它会工作得很好。

ConvertTo-Json @($json) -Depth 10  # good

ulydmbyx

ulydmbyx4#

如果您可以选择使用较新的PowerShell Core,则格式现在已修复。

4urapxun

4urapxun5#

我写了下一个函数来修复缩进

function FixJsonIndentation ($jsonOutput)
{
    $currentIndent = 0
    $tabSize = 4
    $lines = $jsonOutput.Split([Environment]::NewLine)
    $newString = ""
    foreach ($line in $lines)
    {
        # skip empty line
        if ($line.Trim() -eq "") {
            continue
        }

        # if the line with ], or }, reduce indent
        if ($line -match "[\]\}]+\,?\s*$") {
            $currentIndent -= 1
        }

        # add the line with the right indent
        if ($newString -eq "") {
            $newString = $line
        } else {
            $spaces = ""
            $matchFirstChar = [regex]::Match($line, '[^\s]+')
            
            $totalSpaces = $currentIndent * $tabSize
            if ($totalSpaces -gt 0) {
                $spaces = " " * $totalSpaces
            }
            
            $newString += [Environment]::NewLine + $spaces + $line.Substring($matchFirstChar.Index)
        }

        # if the line with { or [ increase indent
        if ($line -match "[\[\{]+\s*$") {
            $currentIndent += 1
        }
    }

    return $newString
}

字符串

ryevplcw

ryevplcw6#

更新了the answer from Theo,使其要求JSON输入,因为我只是想美化JSON数据。此外,删除了数学方法调用,因为我不能在我想要使用它的地方使用方法调用。
太感谢你了!

function Format-Json {
    <#
    .SYNOPSIS
        Prettifies JSON output.
    .DESCRIPTION
        Reformats a JSON string so the output looks better than what ConvertTo-Json outputs.
    .PARAMETER Json
        Required: [string] The JSON text to prettify.
    .PARAMETER Minify
        Optional: Returns the json string compressed.
    .PARAMETER Indentation
        Optional: The number of spaces (1..1024) to use for indentation. Defaults to 4.
    .PARAMETER AsArray
        Optional: If set, the output will be in the form of a string array, otherwise a single string is output.
    .EXAMPLE
        $json | ConvertTo-Json  | Format-Json -Indentation 2
    #>
    [CmdletBinding(DefaultParameterSetName = 'Prettify')]
    Param(
        [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
        [string]$Json,

        [Parameter(ParameterSetName = 'Minify')]
        [switch]$Minify,

        [Parameter(ParameterSetName = 'Prettify')]
        [ValidateRange(1, 1024)]
        [int]$Indentation = 4,

        [Parameter(ParameterSetName = 'Prettify')]
        [switch]$AsArray
    )

    if ($PSCmdlet.ParameterSetName -eq 'Minify') {
        return ($Json | ConvertFrom-Json) | ConvertTo-Json -Depth 100 -Compress
    }

    # If the input JSON text has been created with ConvertTo-Json -Compress
    # then we first need to reconvert it without compression
    if ($Json -notmatch '\r?\n') {
        $Json = ($Json | ConvertFrom-Json) | ConvertTo-Json -Depth 100
    }

    $indent = 0
    $regexUnlessQuoted = '(?=([^"]*"[^"]*")*[^"]*$)'

    $result = $Json -split '\r?\n' |
        ForEach-Object {
            # If the line contains a ] or } character, 
            # we need to decrement the indentation level unless it is inside quotes.
            if ($_ -match "[}\]]$regexUnlessQuoted") {

                [int[]] $indentArray = ($indent - $Indentation),0

                if ($indentArray[0] -gt $indentArray[1]) { 
                    $indent = $indentArray[0] 
                    }
                else { 
                    $indent = 0
                    }
            }

            # Replace all colon-space combinations by ": " unless it is inside quotes.
            $line = (' ' * $indent) + ($_.TrimStart() -replace ":\s+$regexUnlessQuoted", ': ')

            # If the line contains a [ or { character, 
            # we need to increment the indentation level unless it is inside quotes.
            if ($_ -match "[\{\[]$regexUnlessQuoted") {
                $indent += $Indentation
            }

            $line
        }

    if ($AsArray) { return $result }
    return $result -Join [Environment]::NewLine
}

$json = Read-Host "Enter json"

$json | Format-Json

字符串

相关问题