我有以下 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 会将其压缩为仅此一个元素。如果在输出中您希望它再次成为数组,请使用 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
我发现 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
}
如果整体是一个数组,元素是单个json文件,那就尴尬了。
如果您使用管道,它不会被视为数组。
$json | ConvertTo-Json -Depth 10 # bad
并且由于它不是普通数组,因此仅将其作为参数传递是行不通的。
ConvertTo-Json $json -Depth 10 # bad
如果重新创建数组,效果会很好。
ConvertTo-Json @($json) -Depth 10 # good
如果您可以选择使用较新的 PowerShell Core,则格式现已修复。 看到这个答案。
我编写了下一个函数来修复缩进
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
}
更新了 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