45

我正在尝试遍历 PowerShell 中的以下 JSON 文件。

没有具体命名顶部标签(例如 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
   }
}
4

5 回答 5

71

PowerShell 3.0+

在 PowerShell 3.0 及更高版本中(请参阅:确定安装的 PowerShell 版本),您可以使用ConvertFrom-Jsoncmdlet 将 JSON 字符串转换为 PowerShell 数据结构。

这既方便又不幸——方便,因为它很容易使用 JSON,不幸的是因为ConvertFrom-Json它给了你PSCustomObjects,并且它们很难作为键值对进行迭代。

当您知道密钥时,就没有什么可以迭代的了——您只需直接访问它们,例如$result.thisKey.then.thatKey.array[1],就完成了。

但是在这个特定的 JSON 中,键似乎是动态的/提前不知道的,例如"17443"or "17444"。这意味着我们需要一些可以将 a转换为可以理解PSCustomObject的键值列表的东西。foreach

# helper to turn PSCustomObject into a list of key/value pairs
function Get-ObjectMember {
    [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和生成输出对象列表FirstNameLastName

$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-ObjectMember | foreach {
    $_.Value | Get-ObjectMember | 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 为您提供了常规的Dictionaries,它们很容易通过以下方法作为键值对进行迭代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 MB,请相应地设置该JavaScriptSerializer.MaxJsonLength属性


从文件中读取 JSON

如果从文件中读取,请使用Get-Content -Raw -Encoding UTF-8.

  • -Raw因为否则会Get-Content返回一个单独的行数组并且JavaScriptSerializer.DeserializeObject无法处理。最近的 Powershell 版本似乎改进了 .NET 函数参数的类型转换,因此它可能不会在您的系统上出错,但如果确实如此(或只是为了安全起见),请使用-Raw.
  • -Encoding因为在阅读时指定文本文件的编码是明智的,并且UTF-8是 JSON 文件最可能的值。

笔记

  • {items: [{key: 'A', value: 0}, {key: 'B', value: 1}]}当您构建包含具有不可预知键的项目的 JSON 时,更喜欢像over之类的数组结构{'A': 0, 'B': 1}。后者看起来更直观,但更难生成也更难消费。
  • ConvertFrom-Json()PSCustomObject为您提供反映 JSON 字符串中数据的 PowerShell 自定义对象 ( )。
  • 您可以循环使用自定义对象的属性Get-Member -type NoteProperty
  • 您可以使用$object."$propName"语法动态访问对象的属性,或者$object."$(some PS expression)"
  • 您可以创建自己的自定义对象并使用一堆属性初始化它New-Object PSObject -Property @{...},或者[PSCustomObject]@{ .. } `
于 2015-11-04T12:23:24.447 回答
5

这个问题出现了很多。在这种情况下,我们必须循环两次属性。这是我目前的答案。使对象更易于使用。顶层和数据属性都成为“名称”和“值”的数组。您可以使用选择对象计算属性以任何您想要的方式呈现它。在 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

此外,Powershell 7 中的 convertFrom-Json 有一个 -AsHashTable 参数,它为您提供键和值属性。

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

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

$a.keys
name
address

$a.values
joe
here
于 2020-04-19T18:54:15.820 回答
3

首先,我们将使用ConvertFrom-Jsoncmdlet 将 JSON 字符串转换为 PowerShell 数据结构。

然后,为了说明如何遍历嵌套的 PowerShell 数据结构,我们将通过一个简化的示例进行演示。

给定

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

我们的目标是遍历预测中的键值对(即猫和狗)并将它们的值缩短到小数点后 3 位。

解决

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

我们首先遍历预测的所有成员,然后为每个成员分配一个新的成员键和一个 3 位小数位值。

输出

Key Value
--- -----
cat 0.658
dog 0.342
于 2021-06-30T19:08:30.453 回答
0

如上面评论中所述,此任务可以在不借助火箭构造的情况下解决=)

要遍历 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
}
于 2022-02-14T19:56:22.767 回答
-22

这是一个简单的基于正则表达式的解决方案。假设$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 ' ' }
于 2015-11-04T12:15:47.460 回答