遍历 JSON 文件 PowerShell

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/33520699/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 18:08:33  来源:igfitidea点击:

Iterating through a JSON file PowerShell

jsonpowershellpowershell-5.0

提问by Omen9876

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

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

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.

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

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

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

How would I accomplish that?

我将如何做到这一点?

{
   "17443":{
      "sid":"17443",
      "nid":"7728",
      "submitted":"1436175407",
      "data":{
         "3":{
            "value":[
               "Mr"
            ]
         },
         "4":{
            "value":[
               "Hyman"
            ]
         },
         "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.

我可以使用下面的代码访问数据,但我想避免输入 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
   }
}

回答by Tomalak

PowerShell 3.0+

PowerShell 3.0+

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

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

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

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

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.

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

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 PSCustomObjectinto a key-value list that foreachcan understand.

但是在这个特定的 JSON 中,键似乎是动态的/提前未知,例如"17443""17444"。这意味着我们需要一些可以将 aPSCustomObject转换为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"}
    }
}

Now we can traverse the object graph and produce a list of output objects with Title, FirstNameand LastName

现在我们可以遍历对象图并生成一个输出对象列表TitleFirstName并且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": ["Hyman"]},"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
        }
    }
}

Output

输出

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


PowerShell 2.0 / Alternative approach

PowerShell 2.0 / 替代方法

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 classto handle the JSON:

另一种适用于 PowerShell 2.0(不支持上述某些结构)的替代方法是使用 .NET JavaScriptSerializer 类来处理 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:

现在我们可以做一个非常相似的操作——甚至比上面的简单一点,因为 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": ["Hyman"]},"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.MaxJsonLengthpropertyaccordingly.

如果您的 JSON 大于 4 MB,请相应地设置该JavaScriptSerializer.MaxJsonLength属性



On reading JSON from files

从文件中读取 JSON

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

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

  • -Rawbecause otherwise Get-Contentreturns an array of individual lines and JavaScriptSerializer.DeserializeObjectcan'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.
  • -Encodingbecause it's wise to specify a text file's encoding when you read it and UTF-8is the most probable value for JSON files.
  • -Raw因为否则会Get-Content返回一组单独的行并且JavaScriptSerializer.DeserializeObject无法处理。最近的 Powershell 版本似乎改进了 .NET 函数参数的类型转换,因此它可能不会在您的系统上出错,但如果它出错(或只是为了安全),请使用-Raw.
  • -Encoding因为在阅读时指定文本文件的编码是明智的,并且UTF-8是 JSON 文件最可能的值。


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 时,更喜欢使用类似{items: [{key: 'A', value: 0}, {key: 'B', value: 1}]}over的数组结构{'A': 0, 'B': 1}。后者看起来更直观,但它更难生成,也更难消费。
  • ConvertFrom-Json()为您提供一个 PowerShell 自定义对象 ( PSCustomObject),它反映 JSON 字符串中的数据。
  • 您可以循环使用自定义对象的属性 Get-Member -type NoteProperty
  • 您可以使用$object."$propName"语法动态访问对象的属性,或者$object."$(some PS expression)".
  • 您可以创建自己的自定义对象并使用一堆属性对其进行初始化New-Object PSObject -Property @{...},或者[PSCustomObject]@{ .. }`

回答by js2010

This question comes up a lot. In this case we have to loop over properties twice. This is my current answer. Make the object a little easier to work with. Both the top level and the data properties become arrays of "name" and "value". You could use select-object calculated properties to present it any way you want. These json examples seem like badly structured objects to me.

这个问题经常出现。在这种情况下,我们必须循环两次属性。这是我目前的答案。使对象更容易使用。顶层和数据属性都变成了“名称”和“值”的数组。您可以使用选择对象计算属性以您想要的任何方式呈现它。这些 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}
{Hyman}
{Cawles}
{Miss}
{Charlotte}
{Tann}
{Mr}
{John}
{Brokland}

Trying something similar with jq:

用 jq 尝试类似的东西:

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

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

回答by Alexander Obersht

Here's a simple regex-based solution. Assuming that $sRawJsoncontains your JSON input:

这是一个简单的基于正则表达式的解决方案。假设$sRawJson包含您的 JSON 输入:

$oRegex = [Regex]'(?:(?<="[345]":\{"value"\:\["))[^"]+'
$cParts = $oRegex.Matches(($sRawJson -replace '\s')) | Select-Object -ExpandProperty "Value"

Joining parts to get full names:

加入零件以获得全名:

for ($i = 0; $i -lt $cParts.Count / 3; $i++) { $cParts[($i * 3)..($i * 3 + 2)] -join ' ' }