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-Jsonコマンドレットを使用して JSON 文字列を PowerShell データ構造に変換できます。

これは便利であると同時に残念なことです。JSON を使用するのは非常に簡単なので便利ですが、 PSCustomObjectsConvertFrom-Jsonが得られるのは残念なことであり、それらはキーと値のペアとして繰り返し処理するのが難しいためです。

キーがわかっている場合は、反復する必要はありません。たとえば、キーに直接アクセスするだけで$result.thisKey.then.thatKey.array[1]完了です。

"17443"しかし、この特定の JSON では、キーは動的であるか、またはのように事前に知られていないよう"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 ファイルの最も可能性の高い値であるためです。

ノート

  • 予測できないキーを持つアイテムを含む JSON を構築する場合は、よりも配列構造を優先{items: [{key: 'A', value: 0}, {key: 'B', value: 1}]}して{'A': 0, 'B': 1}ください。後者の方が直感的に見えますが、生成するのも消費するのも難しいです。
  • ConvertFrom-Json()PSCustomObjectJSON 文字列のデータを反映する PowerShell カスタム オブジェクト ( ) を提供します。
  • カスタムオブジェクトのプロパティをループできますGet-Member -type NoteProperty
  • $object."$propName"構文、alternativeを使用して、オブジェクトのプロパティに動的にアクセスできます$object."$(some PS expression)"
  • 独自のカスタム オブジェクトを作成し、一連のプロパティで初期化できNew-Object PSObject -Property @{...}ます[PSCustomObject]@{ .. }
于 2015-11-04T12:23:24.447 に答える
3

まず、ConvertFrom-Jsonコマンドレットを使用して 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 に答える