2011/05/15
JavaScriptSerializerを使ってJSONをパース/作成
PowerShellでJScript.NETを利用してJSONをパースするの続きです。
あれからまた色々調べていると、.NET Framework 3.5から追加されたSystem.Web.Script.Serialization.JavaScriptSerializerクラスを用いるとJSONのパースと作成が簡単に行えることがわかりました。
まずはパースから。
$json=@' {"items": [ { "code":25, "name":"ハードディスク2TB", "price":7000 }, { "code":56, "name":"メモリ8GB", "price":8000 }, { "code":137, "name":"23インチ液晶ディスプレイ", "price":35000 } ] } '@ Add-Type -AssemblyName System.Web.Extensions $serializer=new-object System.Web.Script.Serialization.JavaScriptSerializer $obj=$serializer.DeserializeObject($json) $obj["items"][1]["name"] #「メモリ8GB」と表示される $obj.items[1].name # 上と同じ $obj["items"]|%{$_["name"]} # 名前が列挙される
このように、JavaScriptSerializerをNew-Objectして、DeserializeObjectメソッドを呼ぶだけで、JSONをパースした結果がオブジェクトに格納されます。このときJSオブジェクトはDictionary<string,object>に、JS配列はobject[]にされて格納されます。よってオブジェクトのアクセスは前回JScript.NETを使った場合と同様にパラメータ付プロパティでできますし、配列のアクセスは数値の添え字で可能です。
前回に比べて良くなっている点は、DictionaryなのでPowerShellにおける連想配列と同様に、プロパティアクセスが可能である点です。よって、$obj.items[1].nameのようにドット演算子で楽に値を取得できます。
さらにJS配列はオブジェクト配列として格納されています。よって、foreachすればその要素がそのまま列挙できます。前回に比べて素直なコードになっていることが分かると思います。
.NET 3.5が使える環境ではPowerShellでJSONをパースするにはJavaScriptSerializerが本命なんじゃないかなと思います。
そしてJavaScriptSerializerクラスを使うと、JSON文字列を作成することも容易です。ここでは先程の例のJSONを逆にPowerShellで作ってみます。
Add-Type -AssemblyName System.Web.Extensions $serializer=new-object System.Web.Script.Serialization.JavaScriptSerializer $serializer.Serialize( @{items= @( @{ code=25; name="ハードディスク2TB"; price=7000 }, @{ code=56; name="メモリ8GB"; price=8000 }, @{ code=137; name="23インチ液晶ディスプレイ"; price=35000 } ) })
このように、Serializeメソッドの引数にPowerShellの連想配列を渡すだけです。連想配列の要素に連想配列や配列を含むことで、オブジェクトを構築していきます。
これを実行すると結果は次のようになります。
{"items":[{"name":"ハードディスク2TB","code":25,"price":7000},{"name":"メモリ8GB","code":56,"price":8000},{"name":"23インチ液晶ディスプレイ","code": 137,"price":35000}]}
これは最初に示したJSONとまったく同じであることが分かると思います。このように非常に直感的かつ簡便にJSON文字列を作成することができます。PowerShellの連想配列と配列を使ってオブジェクトを組み立てるだけなので、難しいことを考える必要はなく、柔軟性も高いです。JSONの作成もJavaScriptSerializerが本命でしょうね。JavaScriptSerializerはPowerShellとの相性が抜群です。
元記事:http://blogs.wankuma.com/mutaguchi/archive/2011/05/15/199058.aspx2011/03/25
ジャグ配列も、多次元配列も、あるんだよ
PowerShellは一次元のobject配列を多用しますが、実は他言語と同様に、多次元配列やジャグ配列(配列の配列)もちゃんとあります。
ジャグ配列
ジャグ配列の作成
PS > $array = @(@("a","b"),@("c","d","e"))
配列の参照
PS > $array a b c d e PS > $array.Length 2 PS > $array[0] a b PS > $array[0][1] b
ジャグ配列の作成は、配列をまず作って、その配列を要素に持つ配列を作成するとできます。とても直感的かと思います。ただしここで注意しなければならない点は、
PS > $array = @(@("a","b") + @("c","d","e"))
のように、配列を+演算子で連結するのは駄目であるという点です。こうしてしまうと単に一次元配列同士が連結された要素数5の一次元配列が生成されてしまいます。
さて、このようにジャグ配列に含まれる配列とその数があらかじめ分かっている場合はこのように,演算子を使えば問題ありませんが、そうではない場合はちょっと工夫が必要です。たとえば「テキストファイルの一行ごとにchar[]配列を作り、それらを要素に持つジャグ配列を作成する」ことを考えてみましょう。まず最初に駄目な例。
$lines = @(Get-Content file.txt) $array = @() foreach($line in $lines) { $array += [char[]]$line }
これは一見うまくいきそうですが、先ほどと同じパターンで$arrayは単なるchar[]の一次元配列になってしまいます。目的のジャグ配列を得るには次のようにします。
$lines = @(Get-Content file.txt) $array = @() foreach($line in $lines) { $array += ,[char[]]$line }
ここでは配列要素の追加処理の際、配列に「,」を前置することによって「配列要素を展開することなく、配列そのものとして扱う」ようにしています。こうすることで$arrayにはchar[]配列そのものが要素として追加され、結果としてジャグ配列が格納されます。
多次元配列
2x2の多次元配列を作成
PS > $array = New-Object "object[,]" 2,2
要素に値を代入
PS > $array[0,0] = "a" PS > $array[0,1] = "b" PS > $array[1,0] = "c" PS > $array[1,1] = "d"
配列の参照
PS > $array a b c d
配列のスライス
PS > $array[@(0,0)] a PS > $array[@(0,0),@(0,1)] a b PS > $array[@(0,0),@(1,0)] a c
配列の作成の仕方がちょっと気持ち悪いですが、これはサイズ固定の一次元配列を作るときと同じ要領です。
配列のスライスも可能で、切り出したい要素のインデックスを「要素のインデックスを表す配列」で指定します。たとえば$array[0,0]を取り出したい場合は$array[@(0,0)]になるわけですね。複数の要素を切り出したい場合は「要素のインデックスを表す配列」の配列(つまりはジャグ配列)を指定することになります。
おまけ:一次元配列のちょっとしたtips
変数、プロパティ、コマンドレットの戻り値などで、その値が配列かそうでないかが事前に分からない場合でも、必ず配列として処理したい場合には@を用います。
$lines = Get-Content file.txt
Get-Contentコマンドレットはテキストファイルが1行の場合は、$linesには文字列の配列ではなく文字列が格納されます(複数行なら行ごとの文字列が格納された配列になる)。よってこの場合$linesが配列かどうかは事前には分からないことになります。テキストファイルの1行目を取得するつもりで $lines[0] とやっても、もしテキストファイルが1行だった場合は、その1行の一文字目が返ってきてしまいます。このような事態を防ぐには、
$lines = @(Get-Content file.txt)
のようにすると、$linesには必ず配列が格納されます。たとえテキストファイルが1行でも、要素数1の配列になります。
これとは逆のケースで、「配列か非配列かわからないが、必ず非配列として扱いたい」場合は次のようにするのも手でしょう。
$value = @($unknown)[0]
この場合だともし$unknownが配列だったとしても、その1要素目をとってきて$valueに格納してくれます。本来なら$unknownが配列かどうか確かめて、その要素数がいくつであるかなどを確認すべきなんですが、「非配列か要素数1の配列どちらかが返されるパターン」というのはADSIやXMLなど扱う際に意外と多くて、そういう場合に有用かと思います。
JavaScriptの配列操作と同じようなことをする方法
$array = @(1..5) # push(配列末尾に値を追加) $array += 6 # unshift(配列先頭に値を追加) $array = @(0) + $array # shift(配列先頭の値を削除) $array[0]; $array = $array[1..($array.length-1)] # pop(配列末尾の値を削除) $array[$array.length-1]; $array=$array[0..($array.length-2)]元記事:http://blogs.wankuma.com/mutaguchi/archive/2011/03/25/197857.aspx
Copyright © 2005-2018 Daisuke Mutaguchi All rights reserved
mailto: mutaguchi at roy.hi-ho.ne.jp
プライバシーポリシー