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

     }  

5 ответов

Решение

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

Выход

Название Фамилия Фамилия                 
----- --------- --------                 
Мисс шарлотта танн                     
Мистер Джон Брокланд                 

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

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

Название Фамилия Фамилия                 
----- --------- --------                 
Мисс шарлотта танн                     
Мистер Джон Брокланд                 

Если у вас 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 @{...} альтернативно [PSCustomObject]@{ .. } `

Этот вопрос возникает часто. В этом случае мы должны дважды перебирать свойства. Это мой текущий ответ. Сделайте так, чтобы с объектом было легче работать. И верхний уровень, и свойства данных становятся массивами "имя" и "значение". Вы можете использовать вычисляемые свойства объекта select, чтобы представить его как хотите. Похоже, что в json чаще получаются случайные свойства, нежели массивы с аналогичным набором свойств.

$a = cat file.json | convertfrom-json

$a = $a.psobject.properties | select name,value 
$a | foreach { $_.value.data = 
  $_.value.data.psobject.properties | select name,value }

$a.value.data.value

value
-----
{Mr}
{Jack}
{Cawles}
{Miss}
{Charlotte}
{Tann}
{Mr}
{John}
{Brokland}

Пробуем что-то подобное с jq:

'{"prop1":1, "prop2":2, "prop3":3}' | jq to_entries | convertfrom-json

key   value
---   -----
prop1     1
prop2     2
prop3     3

Также convertFrom-Json в Powershell 7 имеет параметр -AsHashTable, который дает вам свойства ключей и значений.

$a = '{"name":"joe","address":"here"}' | ConvertFrom-Json -AsHashtable
$a

Name                           Value
----                           -----
name                           joe
address                        here

$a.keys
name
address

$a.values
joe
here

Эту задачу можно решить, не прибегая к постройке ракеты, как описано в комментариях выше =)

Чтобы перебрать файл json, мы можем обратиться к переменной, которая является объектом powershell (в котором мы рассмотрели json)

Смотри какие $data.PsObject.Properties там много интересного =)

Решение:

      $data = ConvertFrom-Json $json

foreach ($elem in $data.PsObject.Properties.Value)
{
    Write-Host "Title:" $elem.data.3.value
    Write-Host "First Name:" $elem.data.4.value
    Write-Host "Surname:" $elem.data.5.value
}

Сначала мы будем использовать ConvertFrom-Json командлет для преобразования строки JSON в структуру данных PowerShell.

Затем, чтобы проиллюстрировать, как перебирать вложенную структуру данных PowerShell, мы продемонстрируем это на упрощенном примере.

Дано

      $response = [PSCustomObject] @{
    prediction = [PSCustomObject] @{
        cat = 0.6576587659
        dog = 0.3423412341
    }
}

Наша цель - перебрать пары ключ-значение в прогнозе (например, кошка и собака) и сократить их значения до трех знаков после запятой.

Решать

      $response.prediction | Get-Member -MemberType NoteProperty | ForEach-Object {
    $key = $_.Name
    [PSCustomObject]@{Key = $key; Value = "{0:N3}" -f $response.prediction.$key}
}

Сначала мы перебираем все элементы предсказания, а затем для каждого из них мы назначаем новый ключ элемента и трехзначное десятичное значение.

Выход

      Key Value
--- -----
cat 0.658
dog 0.342

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