Как изменить ширину табуляции при конвертации в JSON в Powershell

Я создаю JSON в Powershell, и я хочу установить собственную ширину вкладки при ее создании (вместо стандартных пробелов по умолчанию я хочу установить только 2 пробела).

Я делаю это, потому что:

  • фактический JSON (не тот, который представлен в приведенном ниже примере) довольно большой (100k + rows), и если он не архивирован, размер его довольно большой; Если я уменьшу ширину табуляции, заметьте уменьшение размера.
  • фактический JSON имеет глубину 5+ узлов!
  • Я не могу использовать -Compress, так как JSON должен быть читаемым человеком
  • Да, я согласен, если он заархивирован, размер значительно сокращается, но мне он также нужен.

Пример кода:

$object = New-Object PSObject
Add-Member -InputObject $object -MemberType NoteProperty -Name Phone -Value "SomePhone"
Add-Member -InputObject $object -MemberType NoteProperty -Name Description -Value "Lorem ipsum dolor.."
Add-Member -InputObject $object -MemberType NoteProperty -Name Price -Value 99.99

$object | ConvertTo-Json

Результат с шириной табуляции = 4 символа пробела.

{
    "Phone":  "SomePhone",
    "Description":  "Lorem ipsum dolor..",
    "Price":  99.99
}

Я попробовал сжатие, но он не дает контроля над уровнем сжатия (как должно быть агрессивное сжатие)

$object | ConvertTo-Json -Compress

Результат сжат, очевидно.

{"Phone":"SomePhone","Description":"Lorem ipsum dolor..","Price":99.99}

То, что я пытаюсь достичь: результат с шириной табуляции = 2 символа пробела.

{
  "Phone":  "SomePhone",
  "Description":  "Lorem ipsum dolor..",
  "Price":  99.99
}

То, что я пробовал до сих пор, приведено ниже в псевдокоде. Я все еще в курсе. Пожалуйста, вытащите меня оттуда:)

while (1) {
    Google, StackOverflow
    Try Stuff found 
    Tweak stuff found

    if (Correct answer) {
        break
    }
}

Ответ 1

Следующий код уменьшит вдвое размер отступа:

$json = @"
{
    "Phone":  "SomePhone",
    "Description":  "Lorem ipsum dolor..",
    "Price":  99.99
}
"@

($json -split '\r\n' |
% {
  $line = $_
  if ($_ -match '^ +') {
    $len  = $Matches[0].Length / 2
    $line = ' ' * $len + $line.TrimStart()
  }
  $line
}) -join "`r`n"

Ответ 2

Поскольку PowerShell ConvertTo-Json создает недетерминированные отступы, текущие ответы не приведут к JSON, который имеет ровно два пробела для каждой глубины в структуре данных.

Чтобы получить отступ для каждого уровня вложенных данных ровно на два пробела больше, чем для уровня вложенности, требуется перестройка отступа. (Похоже, это было исправлено в PowerShell 6)

После написания своего собственного решения я нашел почти идентичное решение на GitHub из Facebook, Daniel Lo Nigro (Daniel15) здесь. Это функция PowerShell, которая может принимать ввод по каналу. (Я сделал сопоставление регулярных выражений более конкретным, чтобы уменьшить вероятность непреднамеренного сопоставления данных.)

# Formats JSON in a nicer format than the built-in ConvertTo-Json does.
function Format-Json([Parameter(Mandatory, ValueFromPipeline)][String] $json) {
    $indent = 0;
    ($json -Split "'n" | % {
        if ($_ -match '[\}\]]\s*,?\s*$') {
            # This line ends with ] or }, decrement the indentation level
            $indent--
        }
        $line = ('  ' * $indent) + $($_.TrimStart() -replace '":  (["{[])', '": $1' -replace ':  ', ': ')
        if ($_ -match '[\{\[]\s*$') {
            # This line ends with [ or {, increment the indentation level
            $indent++
        }
        $line
    }) -Join "'n"
}

Использование: $foo | ConvertTo-Json | Format-Json

Ответ 3

Вот простой метод:

$data_json | convertto-json -depth 100 |
    foreach-object {$_ -replace "(?m)  (?<=^(?:  )*)", "'t" } |
    set-content 'output.json'

Объект foreach перехватывает, если вы передаете более одного объекта в ConvertTo-JSON.

Измените "'t" на то, что вы хотите сделать с отступом.

Ответ 4

Вы можете использовать Newtonsoft.Json с PowerShell. Для этого в галерее PowerShell есть модуль, который вы можете установить, чтобы сделать его доступным удобным способом.

Пример:

if (!(Get-Module -ListAvailable -Name "newtonsoft.json")) {
    Install-Module -Name "newtonsoft.json" -Scope CurrentUser -Force
}

Import-Module "newtonsoft.json" -Scope Local

$JObject = [Newtonsoft.Json.Linq.JObject]::new(
    [Newtonsoft.Json.Linq.JProperty]::new("Phone", "SomePhone"),
    [Newtonsoft.Json.Linq.JProperty]::new("Description", "Lorem ipsum dolor.."),
    [Newtonsoft.Json.Linq.JProperty]::new("Price", 99.99));

$JObject.ToString()

Производит

{
  "Phone": "SomePhone",
  "Description": "Lorem ipsum dolor..",
  "Price": 99.99
}

Он также имеет множество других функций при работе с json: https://www.newtonsoft.com/json/help/html/Introduction.htm