Итерация через 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
        }

     }  

2 ответов


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 / Альтернатива подходи!--32-->

альтернативный подход, который также работает для 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 улучшились type-conversion для аргументов функции .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 @{...}, либо [PSCustomObject]@{ .. } `

вот простое решение на основе регулярных выражений. Предполагая, что $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 ' ' }