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