JSONは、Web APIのやり取りや設定ファイルで最もよく使われるデータ形式です。PowerShellには、JSONを扱う2つのコマンドが標準で用意されています。文字列のJSONをオブジェクトに変換するConvertFrom-Jsonと、オブジェクトをJSON文字列に変換するConvertTo-Jsonです。
基本はこの2つを覚えるだけですが、ConvertTo-Jsonの既定のネスト深さが「2」しかなく、深い階層が勝手に切り捨てられるという、知らないと必ずハマる落とし穴があります。この記事では、読み込み・書き出し・APIレスポンス処理までを、実機のPowerShellで確認しながら解説します。
- JSON文字列をオブジェクトにするには
ConvertFrom-Json。結果はPSCustomObjectで、$obj.nameのように読めます。 - オブジェクトをJSONにするには
ConvertTo-Json。ハッシュテーブルやオブジェクト配列も変換できます。 - 最重要:
ConvertTo-Jsonの既定-Depthは2です。深いネストは切れるので、-Depth 10のように明示します。 - 1行にまとめるなら
-Compressを付けます。 - ファイルから読むときは
Get-Content -Rawで全体を1つの文字列として渡すのが確実です。 - APIのJSONレスポンスは、
Invoke-RestMethodなら自動でオブジェクトに変換されます。
JSONはハッシュテーブルやオブジェクトと密接に関わります。配列とハッシュテーブルの使い方、ファイル保存時のエンコードは文字化けを直す方法とあわせて読むと理解が深まります。同じくデータ形式を扱うCSVを読み書きする方法も、JSONと使い分ける場面で参考になります。
ConvertFrom-JsonでJSONを読み込む
JSON形式の文字列をConvertFrom-Jsonに渡すと、PSCustomObjectというオブジェクトに変換されます。あとは普通のオブジェクトと同じく、ドット記法でプロパティを読み出せます。
$json = '{"name":"山田","age":30}'
$obj = $json | ConvertFrom-Json
$obj.name # 山田
$obj.age # 30
$obj.GetType().Name # PSCustomObject
実機でも、$obj.nameが山田、型がPSCustomObjectになることを確認しています。JSONのキーが、そのままオブジェクトのプロパティ名になります。
ネストや配列を含むJSONにアクセスする
入れ子になったオブジェクトや配列も、そのまま辿れます。ネストはドットでつなぎ、配列はインデックスで取り出します。
$json = '{
"name": "山田",
"address": { "city": "東京", "zip": "100-0001" },
"hobbies": ["読書", "旅行"]
}'
$obj = $json | ConvertFrom-Json
$obj.address.city # 東京(ネストをドットで辿る)
$obj.hobbies[0] # 読書(配列はインデックス)
# 配列をループ処理する
foreach ($hobby in $obj.hobbies) {
Write-Host $hobby
}
ネストは$obj.address.cityのようにドットでつなぐだけ、配列は$obj.hobbies[0]のようにインデックスで取り出せます。配列の各要素はforeachで回せます。
ファイルからJSONを読み込む
JSONファイルを読むときは、Get-Contentで読み込んでからConvertFrom-Jsonに渡します。このとき、-Rawを付けてファイル全体を1つの文字列として渡すのが確実です。
# -Raw でファイル全体を1つの文字列として読む(推奨)
$config = Get-Content "C:\work\config.json" -Raw | ConvertFrom-Json
$config.name
# 文字化けする場合はエンコードも明示する
$config = Get-Content "C:\work\config.json" -Raw -Encoding UTF8 |
ConvertFrom-Json
-Rawを付けないと、Get-Contentはファイルを1行ずつの配列として読み込みます。環境やバージョンによっては、この複数行のまま渡すとJSONの解析に失敗することがあります。-Rawでファイル全体を1つの文字列にしてから渡せば、確実かつ効率的です。読み込み時に文字化けする場合は、-Encoding UTF8もあわせて指定してください。
ConvertTo-JsonでオブジェクトをJSONにする
逆に、PowerShellのオブジェクトをJSON文字列にするにはConvertTo-Jsonを使います。ハッシュテーブルでもPSCustomObjectでも変換できます。
# ハッシュテーブルから
@{ name = "山田"; age = 30 } | ConvertTo-Json
# PSCustomObject から
[PSCustomObject]@{ name = "山田"; age = 30 } | ConvertTo-Json
# オブジェクトの配列から(JSON配列になる)
$people = @(
[PSCustomObject]@{ name = "A"; score = 90 }
[PSCustomObject]@{ name = "B"; score = 80 }
)
$people | ConvertTo-Json
オブジェクトの配列を渡すと、JSONの配列[ ... ]になります。実機でも、2件のオブジェクト配列が[{"name":"A","score":90},{"name":"B","score":80}]に変換されることを確認しています。
【最重要】既定のDepthは2、深いネストは切れる
ここがPowerShellのJSONで最もハマるポイントです。ConvertTo-Jsonは、既定では2階層までしかネストを展開しません。それより深い部分は、中身ではなく"System.Collections.Hashtable"のような型名の文字列に化けてしまいます。
$data = @{ a = @{ b = @{ c = @{ d = "深い値" } } } }
# 既定(-Depth 2)だと深い階層が切れる
$data | ConvertTo-Json
# 出力例:
# {
# "a": { "b": { "c": "System.Collections.Hashtable" } }
# } ← d の値が失われている
# -Depth を十分に大きく指定すると、全階層が出る
$data | ConvertTo-Json -Depth 5
# {"a":{"b":{"c":{"d":"深い値"}}}}
実機で確認したところ、既定のままでは深い階層の値が"System.Collections.Hashtable"という文字列に置き換わり、データが失われました。エラーは出ないため気づきにくく、出力されたJSONを他システムへ渡してから発覚する、という事故になりがちです。ネストのあるデータは、-Depth 10のように余裕をもった深さを必ず指定してください(最大100まで指定できます)。
1行にまとめる -Compress と日本語の扱い
既定のConvertTo-Jsonは、見やすいように改行とインデントを入れた整形済みのJSONを出します。APIへ送るときなど1行にまとめたい場合は、-Compressを付けます。
@{ name = "山田"; age = 30 } | ConvertTo-Json -Compress
# {"age":30,"name":"山田"} ← 改行なしの1行になる
日本語の扱いには補足があります。ConvertTo-Jsonは、環境やバージョンによって、日本語などの非ASCII文字を\u5c71\u7530のようなUnicodeエスケープで出力することがあります(今回テストしたWindows PowerShell 5.1のビルドでは、山田がそのまま出力されました)。エスケープされていても正しいJSONであり、読み戻せば元の文字に戻るため、データとしては問題ありません。生のまま出したい場合は、エスケープを行わないPowerShell 7以降を使う方法があります。なお、ファイルに保存したときに画面表示が崩れる場合は、JSONのエスケープではなくファイルのエンコードが原因のことが多いです。
JSONファイルへ書き出す(エンコードに注意)
変換したJSONをファイルに保存するときは、エンコードに注意します。Windows PowerShell 5.1の既定では意図しない文字コードになりやすいため、-Encodingを明示します。
$data = [PSCustomObject]@{
name = "山田"
age = 30
address = [PSCustomObject]@{ city = "東京" }
}
# ネストがあるので -Depth を指定し、UTF-8 で保存する
$data | ConvertTo-Json -Depth 10 |
Out-File "C:\work\out.json" -Encoding UTF8
# BOM無しUTF-8が必要なら .NET で書き出す(PHPなど他ツール向け)
$json = $data | ConvertTo-Json -Depth 10
$utf8NoBom = New-Object System.Text.UTF8Encoding $false
[System.IO.File]::WriteAllText("C:\work\out.json", $json, $utf8NoBom)
5.1の-Encoding UTF8はBOM付きになるため、BOMを嫌う相手(PHPや一部のCLI)に渡すなら、.NETのWriteAllTextでBOM無しUTF-8にします。このあたりの詳細は文字化けを直す方法で解説しています。
値を変更してJSONを更新する
設定ファイルを読み込んで一部の値を書き換え、保存し直す、というのはよくある処理です。読み込んだPSCustomObjectのプロパティはそのまま変更でき、新しいプロパティはAdd-Memberで追加します。
$obj = '{"name":"山田","age":30}' | ConvertFrom-Json
# 既存プロパティの変更
$obj.age = 31
# プロパティの追加
$obj | Add-Member -NotePropertyName "city" -NotePropertyValue "東京"
# JSONに戻す
$obj | ConvertTo-Json -Compress
# {"name":"山田","age":31,"city":"東京"}
実機でも、値の変更とAdd-Memberでの追加を行ったうえでConvertTo-Jsonに戻すと、変更が反映されたJSONが得られました。設定ファイルの更新は「読む→変える→-Depthを付けて書き戻す」という流れになります。
APIのJSONレスポンスを扱う(Invoke-RestMethod)
Web APIからJSONを受け取る場合、Invoke-RestMethodを使うとレスポンスを自動でオブジェクトに変換してくれます。ConvertFrom-Jsonを自分で呼ぶ必要はありません。
# Invoke-RestMethod は JSON を自動でオブジェクト化する
$result = Invoke-RestMethod -Uri "https://api.example.com/users/1"
$result.name # そのままプロパティにアクセスできる
# JSON を送る POST(オブジェクトを ConvertTo-Json して body に)
$body = @{ name = "山田"; age = 30 } | ConvertTo-Json
$response = Invoke-RestMethod -Uri "https://api.example.com/users" `
-Method Post -Body $body -ContentType "application/json"
# Invoke-WebRequest を使う場合は .Content を手動で変換する
$res = Invoke-WebRequest -Uri "https://api.example.com/users/1"
$obj = $res.Content | ConvertFrom-Json
受け取り(GET)ではInvoke-RestMethodが変換まで担い、送信(POST)ではConvertTo-JsonでJSON文字列を作って-Bodyに渡します。Invoke-WebRequestを使う場合は、レスポンスの.Contentを自分でConvertFrom-Jsonします。
キーの順序を保つには [ordered] を使う
ハッシュテーブルからJSONを作ると、キーの順番が書いた順と変わることがあります。実機でも、@{ enabled=...; max=... }が{"max":...,"enabled":...}と入れ替わりました。JSONのキー順を保ちたいときは、[ordered]付きのハッシュテーブルかPSCustomObjectを使います。
# 通常のハッシュテーブルは順序が保証されない
@{ id = 1; name = "山田"; age = 30 } | ConvertTo-Json -Compress
# [ordered] なら書いた順を保つ
[ordered]@{ id = 1; name = "山田"; age = 30 } | ConvertTo-Json -Compress
# {"id":1,"name":"山田","age":30}
# PSCustomObject も順序を保つ
[PSCustomObject]@{ id = 1; name = "山田"; age = 30 } | ConvertTo-Json -Compress
キー順が崩れて困るのは、人が読むファイルや、順序を前提にした相手に渡す場合です。[ordered]やPSCustomObjectの使い分けは配列とハッシュテーブルの使い方でも触れています。
よくある失敗
ConvertTo-JsonでDepthを指定せず深い階層が消える
既定の-Depthは2です。ネストのあるデータは、深い部分が型名の文字列に化けて失われます。エラーが出ないため気づきにくいので、-Depth 10などを必ず指定してください。
ファイルを-Rawなしで読んで解析に失敗する
Get-Contentは既定で1行ずつの配列を返します。環境によっては解析に失敗するため、-Rawで全体を1つの文字列にしてからConvertFrom-Jsonへ渡します。
保存したJSONが文字化けする
これはJSON変換ではなく、ファイルのエンコードが原因です。5.1の既定エンコードは意図しない文字コードになりやすいので、-Encoding UTF8やBOM無しUTF-8で保存します。
ハッシュテーブルのキー順が変わって困る
通常のハッシュテーブルは順序を保証しません。順番が重要なら[ordered]@{}かPSCustomObjectを使います。
APIレスポンスにConvertFrom-Jsonを二重にかける
Invoke-RestMethodはすでにオブジェクトを返します。これにさらにConvertFrom-Jsonをかけるとエラーになります。手動変換が必要なのはInvoke-WebRequestの.Contentのほうです。
よくある質問
ConvertFrom-Jsonは「JSON文字列からオブジェクトへ」(読み込み)、ConvertTo-Jsonは「オブジェクトを JSON へ」(書き出し)です。From=入力、To=出力と覚えると迷いません。-Depth 10程度、複雑なデータなら-Depth 20などにしておくと安全です。最大100まで指定できます。読み込み側のConvertFrom-Jsonには深さ制限の心配は基本的にありません。ConvertFrom-Jsonでオブジェクトにしてから、$obj.プロパティ名や$obj.配列[0]で辿ります。配列の中から条件で絞るならWhere-Objectを使います。Invoke-RestMethodはJSONなどのレスポンスを自動でオブジェクトに変換します。Invoke-WebRequestはステータスコードやヘッダーを含む生のレスポンスを返し、本文は.Contentから取り出して自分で変換します。APIのJSONを扱うなら前者が手軽です。まとめ
ConvertFrom-JsonでJSON文字列をPSCustomObjectにし、$obj.nameのように読みます。- ネストはドット、配列はインデックスで辿れます。ファイルは
Get-Content -Rawで読みます。 ConvertTo-JsonでオブジェクトをJSONにします。既定Depthは2なので、ネストがあれば-Depthを必ず指定します。- 1行にするなら
-Compress、保存時は-Encodingで文字化けを防ぎます。 - キー順を保つなら
[ordered]かPSCustomObjectを使います。 - APIのJSONは
Invoke-RestMethodが自動でオブジェクト化します。
PowerShellのJSON処理はConvertFrom-JsonとConvertTo-Jsonの2つが基本で、覚えること自体は多くありません。ただし-Depthの既定値が2という落とし穴だけは、データを静かに失うため要注意です。ネストのあるデータには必ず-Depthを指定する、と習慣づけておけば安心です。

