遍历 JSON 文件 PowerShell [英] Iterating through a JSON file PowerShell

查看:27
本文介绍了遍历 JSON 文件 PowerShell的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

没有特别命名顶部标签(例如 17443 和 17444),因为我事先不知道它们,所以我找不到循环数据的方法.

我想为所有记录输出标签 3、4 和 5(标题、名字、姓氏).

我将如何做到这一点?

<代码>{17443":{"sid":"17443","nid":"7728","已提交":"1436175407",数据":{3":{价值":[先生"]},4":{价值":[杰克"]},5":{价值":[考尔斯"]}} },17444":{"sid":"17444","nid":"7728","已提交":"1436891400",数据":{3":{价值":[错过"]},4":{价值":[《夏洛特》]},5":{价值":[坦"]}}},17445":{"sid":"17445","nid":"7728","已提交":"1437142325",数据":{3":{价值":[先生"]},4":{价值":[约翰"]},5":{价值":[布罗克兰"]}}}}

我可以使用下面的代码访问数据,但我想避免输入 17443、17444 等

$data = ConvertFrom-Json $jsonforeach($data.17443 中的 $i){foreach($i.data.3 中的 $t){写主机 $t.value}foreach($i.data.4 中的 $t){写主机 $t.value}foreach($i.data.5 中的 $t){写主机 $t.value}}

解决方案

PowerShell 3.0+

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

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

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

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

# helper 将 PSCustomObject 转换为键/值对列表函数 Get-ObjectMember {[CmdletBinding()]参数([参数(强制=$True,ValueFromPipeline=$True)][PSCustomObject]$obj)$obj |Get-Member -MemberType NoteProperty |ForEach-Object {$key = $_.Name[PSCustomObject]@{Key = $key;值 = $obj.$key"}}}

现在我们可以遍历对象图并生成带有TitleFirstNameLastName

的输出对象列表

$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",提交":1436175407",;数据":{3":{价值":[先生"]},4":{价值":[杰克"]},5":{";值":[考尔斯"]}}}}'$json |ConvertFrom-Json |获取对象成员 |foreach{$_.值 |获取对象成员 |其中 Key -match "^d+$";|foreach{[PSCustomObject]@{标题 = $_.value.data."3".value |选择-第一个 1FirstName = $_.Value.data.4".value |选择-第一个 1姓氏 = $_.Value.data.5".value |选择-第一个 1}}}

输出

<前>标题名字姓氏----- --------- --------夏洛特·坦恩小姐约翰·布罗克兰先生


PowerShell 2.0/替代方法

另一种适用于 PowerShell 2.0(不支持上述某些结构)的替代方法将涉及使用 .NET JavaScriptSerializer 类来处理 JSON:

Add-Type -AssemblyName System.Web.Extensions$JS = 新对象 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",提交":1436175407",;数据":{3":{价值":[先生"]},4":{价值":[杰克"]},5":{";值":[考尔斯"]}}}}'$data = $JS.DeserializeObject($json)$data.GetEnumerator() |foreach{$_.Value.GetEnumerator() |where { $_.Key -match "^d+$";} |foreach{新对象 PSObject -Property @{标题 = $_.Value.data.3".value |选择-第一个 1FirstName = $_.Value.data.4".value |选择-第一个 1姓氏 = $_.Value.data.5".value |选择-第一个 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 文件最可能的值.

注意事项

  • 当您构建包含具有不可预测键的项目的 JSON 时,更喜欢像 {items: [{key: 'A', value: 0}, {key: 'B', value: 1}] 这样的数组结构} 超过 {'A': 0, 'B': 1}.后者看起来更直观,但更难生成,也更难消费.
  • ConvertFrom-Json() 为您提供一个 PowerShell 自定义对象 (PSCustomObject),它反映了 JSON 字符串中的数据.
  • 您可以使用 Get-Member -type NoteProperty
  • 遍历自定义对象的属性
  • 您可以使用$object."$propName"语法动态访问对象的属性,或者$object."$(some PS expression)"代码>.
  • 您可以创建自己的自定义对象并使用New-Object PSObject -Property @{...}[PSCustomObject]@{ ..}`

I am trying to loop through the below JSON file in PowerShell.

Without specifically naming the top tags (e.g. 17443 and 17444), as I do not know them in advance I cannot find a way to loop through the data.

I want to output tags 3, 4 and 5 (title, firstname, surname) for all the records.

How would I accomplish that?

{
   "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"
               ]
            }
         }
      }
   }

I can access the data with the code below, but I want to avoid putting in 17443, 17444, etc.

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

解决方案

PowerShell 3.0+

In PowerShell 3.0 and higher (see: Determine installed PowerShell version) you can use the ConvertFrom-Json cmdlet to convert a JSON string into a PowerShell data structure.

That's convenient and unfortunate at the same time - convenient, because it's very easy to consume JSON, unfortunate because ConvertFrom-Json gives you PSCustomObjects, and they are hard to iterate over as key-value pairs.

When you know the keys then there is nothing to iterate - you just access them directly, e.g. $result.thisKey.then.thatKey.array[1], and you're done.

But in this particular JSON, the keys seem to be dynamic/not known ahead of time, like "17443" or "17444". That means we need something that can turn a PSCustomObject into a key-value list that foreach can understand.

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

Now we can traverse the object graph and produce a list of output objects with Title, FirstName and 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-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
        }
    }
}

Output

Title                      FirstName                  LastName                 
-----                      ---------                  --------                 
Miss                       Charlotte                  Tann                     
Mr                         John                       Brokland                 


PowerShell 2.0 / Alternative approach

An alternative approach that also works for PowerShell 2.0 (which does not support some of the constructs above) would involve using the .NET JavaScriptSerializer class to handle the JSON:

Add-Type -AssemblyName System.Web.Extensions
$JS = New-Object System.Web.Script.Serialization.JavaScriptSerializer

Now we can do a very similar operation—even a bit simpler than above, because JavaScriptSerializer gives you regular Dictionaries, which are easy to iterate over as key-value pairs via the GetEnumerator() method:

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

The output is the same:

Title                      FirstName                  LastName                 
-----                      ---------                  --------                 
Miss                       Charlotte                  Tann                     
Mr                         John                       Brokland                 

If you have JSON larger than 4 MB, set the JavaScriptSerializer.MaxJsonLength property accordingly.


On reading JSON from files

If you read from a file, use Get-Content -Raw -Encoding UTF-8.

  • -Raw because otherwise Get-Content returns an array of individual lines and JavaScriptSerializer.DeserializeObject can't handle that. Recent Powershell versions seem to have improved type-conversion for .NET function arguments, so it might not error out on your system, but if it does (or just to be safe), use -Raw.
  • -Encoding because it's wise to specify a text file's encoding when you read it and UTF-8 is the most probable value for JSON files.

Notes

  • When you build JSON that contains items with unpredictable keys, prefer an array structure like {items: [{key: 'A', value: 0}, {key: 'B', value: 1}]} over {'A': 0, 'B': 1}. The latter seems more intuitive, but it's both harder to generate and harder to consume.
  • ConvertFrom-Json() gives you a PowerShell custom object (PSCustomObject) that reflects the data in the JSON string.
  • You can loop though the properties of a custom object with Get-Member -type NoteProperty
  • You can access the properties of an object dynamically using the $object."$propName" syntax, alternatively $object."$(some PS expression)".
  • You can create your own custom object and initialize it with a bunch of properties with New-Object PSObject -Property @{...}, alternatively [PSCustomObject]@{ .. } `

这篇关于遍历 JSON 文件 PowerShell的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆