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