使用powershell正确格式化JSON

问题描述 投票:0回答:6

我有以下 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 中保留源格式?

json powershell formatting
6个回答
35
投票

由于您的原始 json 包含一个只有一个元素的数组,PowerShell 会将其压缩为仅此一个元素。如果在输出中您希望它再次成为数组,请使用 rokumaru 的好答案

但是,PowerShell 的

ConvertTo-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") {
                $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.
            if ($_ -match "[\{\[]$regexUnlessQuoted") {
                $indent += $Indentation
            }

            $line
        }

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

像这样使用它:

$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

6
投票

我发现 Theo 的出色答案 导致空 [] 或 {} 或内联数组的缩进错误。

输入示例:

$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
}

5
投票

如果整体是一个数组,元素是单个json文件,那就尴尬了。
如果您使用管道,它不会被视为数组。

$json | ConvertTo-Json -Depth 10 # bad

并且由于它不是普通数组,因此仅将其作为参数传递是行不通的。

ConvertTo-Json $json -Depth 10  # bad

如果重新创建数组,效果会很好。

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

5
投票

如果您可以选择使用较新的 PowerShell Core,则格式现已修复。 看到这个答案


4
投票

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

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
}

3
投票

更新了 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

© www.soinside.com 2019 - 2024. All rights reserved.