Итерация по файлу JSON PowerShell

Я пытаюсь перебрать файл JSON ниже в PowerShell.

Без особого присвоения имен верхним тэгам (например, 17443 и 17444), поскольку я не знаю их заранее, я не могу найти способ перебрать данные.

Я хочу вывести теги 3, 4 и 5 (заголовок, имя, фамилия) для всех записей.

Как мне это сделать?

{
   "17443":{
      "sid":"17443",
      "nid":"7728",
      "submitted":"1436175407",
      "data":{
         "3":{
            "value":[
               "Mr"
            ]
         },
         "4":{
            "value":[
               "Jack"
            ]
         },
         "5":{
            "value":[
               "Cawles"
            ]
         }
      },
      "17444":{
         "sid":"17444",
         "nid":"7728",
         "submitted":"1436891400",
         "data":{
            "3":{
               "value":[
                  "Miss"
               ]
            },
            "4":{
               "value":[
                  "Charlotte"
               ]
            },
            "5":{
               "value":[
                  "Tann"
               ]
            }
         }
      },
      "17445":{
         "sid":"17445",
         "nid":"7728",
         "submitted":"1437142325",
         "data":{
            "3":{
               "value":[
                  "Mr"
               ]
            },
            "4":{
               "value":[
                  "John"
               ]
            },
            "5":{
               "value":[
                  "Brokland"
               ]
            }
         }
      }
   }
}

Я могу получить доступ к данным с помощью приведенного ниже кода, но я хочу избежать ввода 17443, 17444 и т.д.

$data = ConvertFrom-Json $json

foreach ($i in $data.17443)
{
   foreach ($t in $i.data.3)
   {
      Write-Host $t.value
   }
   foreach ($t in $i.data.4)
   {
      Write-Host $t.value
   }
   foreach ($t in $i.data.5)
   {
      Write-Host $t.value
   }
}

Ответ 1

PowerShell 3. 0+

В PowerShell 3.0 и выше (см. Определение установленной версии PowerShell) вы можете использовать командлет ConvertFrom-Json для преобразования строки JSON в структуру данных PowerShell.

Это удобно и неудачно в то же время - удобно, потому что очень легко использовать JSON, к сожалению, потому что ConvertFrom-Json предоставляет вам PSCustomObjects, и их трудно перебирать как пары ключ-значение.

В этом конкретном JSON ключи кажутся динамическими/неизвестными заранее, как "17443" или "17444". Это означает, что нам нужно что-то, что может превратить PSCustomObject в список значений ключа, который может понять foreach.

# helper to turn PSCustomObject into a list of key/value pairs
function Get-ObjectMembers {
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory=$True, ValueFromPipeline=$True)]
        [PSCustomObject]$obj
    )
    $obj | Get-Member -MemberType NoteProperty | ForEach-Object {
        $key = $_.Name
        [PSCustomObject]@{Key = $key; Value = $obj."$key"}
    }
}

Теперь мы можем пройти по графу объектов и создать список выходных объектов с помощью Title, FirstName и LastName

$json = '{"17443": {"17444": {"sid": "17444","nid": "7728","submitted": "1436891400","data": {"3": {"value": ["Miss"]},"4": {"value": ["Charlotte"]},"5": {"value": ["Tann"]}}},"17445": {"sid": "17445","nid": "7728","submitted": "1437142325","data": {"3": {"value": ["Mr"]},"4": {"value": ["John"]},"5": {"value": ["Brokland"]}}},"sid": "17443","nid": "7728","submitted": "1436175407","data": {"3": {"value": ["Mr"]},"4": {"value": ["Jack"]},"5": {"value": ["Cawles"]}}}}'

$json | ConvertFrom-Json | Get-ObjectMembers | foreach {
    $_.Value | Get-ObjectMembers | where Key -match "^\d+$" | foreach {
        [PSCustomObject]@{
            Title = $_.value.data."3".value | select -First 1
            FirstName = $_.Value.data."4".value | select -First 1
            LastName = $_.Value.data."5".value | select -First 1
        }
    }
}

Выход

Title                      FirstName                  LastName                 
-----                      ---------                  --------                 
Miss                       Charlotte                  Tann                     
Mr                         John                       Brokland                 

PowerShell 2.0/Альтернативный подход

Альтернативный подход, который также работает для PowerShell 2.0 (который не поддерживает некоторые из приведенных выше конструкций), предполагает использование класса.NET JavaScriptSerializer для обработки JSON:

Add-Type -AssemblyName System.Web.Extensions
$JS = New-Object System.Web.Script.Serialization.JavaScriptSerializer

Теперь мы можем сделать очень похожую операцию - даже немного проще, чем выше, потому что JavaScriptSerializer предоставляет вам обычные словари, которые легко перебирать в виде пар ключ-значение с помощью метода GetEnumerator():

$json = '{"17443": {"17444": {"sid": "17444","nid": "7728","submitted": "1436891400","data": {"3": {"value": ["Miss"]},"4": {"value": ["Charlotte"]},"5": {"value": ["Tann"]}}},"17445": {"sid": "17445","nid": "7728","submitted": "1437142325","data": {"3": {"value": ["Mr"]},"4": {"value": ["John"]},"5": {"value": ["Brokland"]}}},"sid": "17443","nid": "7728","submitted": "1436175407","data": {"3": {"value": ["Mr"]},"4": {"value": ["Jack"]},"5": {"value": ["Cawles"]}}}}'

$data = $JS.DeserializeObject($json)

$data.GetEnumerator() | foreach {
    $_.Value.GetEnumerator() | where { $_.Key -match "^\d+$" } | foreach {
        New-Object PSObject -Property @{
            Title = $_.Value.data."3".value | select -First 1
            FirstName = $_.Value.data."4".value | select -First 1
            LastName = $_.Value.data."5".value | select -First 1
        }
    }
}

Вывод такой же:

Title                      FirstName                  LastName                 
-----                      ---------                  --------                 
Miss                       Charlotte                  Tann                     
Mr                         John                       Brokland                 

Если размер JSON превышает 4 МБ, соответственно установите свойство JavaScriptSerializer.MaxJsonLength.


При чтении JSON из файлов

Если вы читаете из файла, используйте Get-Content -Raw -Encoding UTF-8.

  • -Raw потому что иначе Get-Content возвращает массив отдельных строк, а JavaScriptSerializer.DeserializeObject не может это обработать. В последних версиях Powershell, по-видимому, улучшено преобразование типов для аргументов функций .NET, поэтому в вашей системе это может не привести к ошибкам, но если это произойдет (или просто будет безопасно), используйте -Raw.
  • -Encoding потому что при чтении целесообразно указывать кодировку текстового файла, а UTF-8 является наиболее вероятным значением для файлов JSON.

Заметки

  • ConvertFrom-Json() предоставляет пользовательский объект PowerShell (PSCustomObject), который отражает данные в строке JSON.
  • Вы можете зациклить свойства пользовательского объекта с помощью Get-Member -type NoteProperty
  • Вы можете получить динамический доступ к свойствам объекта, используя синтаксис $object."$propName", альтернативно $object."$(some PS expression)".
  • Вы можете создать свой собственный объект и инициализировать его с помощью New-Object PSObject -Property @{...} свойств с помощью New-Object PSObject -Property @{...}, альтернативно [PSCustomObject]@{.. } '

Ответ 2

Здесь простое решение на основе регулярных выражений. Предполагая, что $sRawJson содержит ваш вход JSON:

$oRegex = [Regex]'(?:(?<="[345]":\{"value"\:\["))[^"]+'
$cParts = $oRegex.Matches(($sRawJson -replace '\s')) | Select-Object -ExpandProperty "Value"

Объединение частей для получения полных имен:

for ($i = 0; $i -lt $cParts.Count / 3; $i++) { $cParts[($i * 3)..($i * 3 + 2)] -join ' ' }