Power QueryでJSONを読み込むと、最初は表ではなく「Record」「List」と書かれたセルが並びます。これを順に展開すれば表になります。ただ、どこまで展開するか、どの列を行に広げるかを決めずに進めると、行が想定より増えたり、nullだらけの列ができたりします。
この記事では、サンプルのJSONを使って次の内容を説明します。
- RecordとListの見分け方
- 展開の順番
- 行が増える箇所とnullの確かめ方
M言語の文法全般は別の記事で扱います。
先に「1行を何の1件にするか」を決める
例として、次のような注文データを使います。
{
"orders": [
{
"id": "A001",
"date": "2026-09-01",
"customer": { "code": "C01", "name": "取引先A" },
"items": [
{ "sku": "P-100", "qty": 2 },
{ "sku": "P-200", "qty": 1 }
]
},
{
"id": "A002",
"date": "2026-09-02",
"customer": { "code": "C02", "name": "取引先B" },
"items": []
}
]
}
このJSONには「注文」と「明細」の2つの単位があります。1行を注文にするか明細にするかで、展開の仕方が変わります。
| 1行の単位 | itemsの扱い | 行数 |
|---|---|---|
| 注文 | 行には展開しない(件数だけ数える、または別のクエリにする) | 注文数と同じ |
| 明細 | 新しい行に展開する | 明細数(空の注文をどう扱うかで変わる) |
両方が必要なら、注文の表と明細の表を別々のクエリにして、idで結びつけるのがおすすめです。注文情報が明細の数だけ繰り返される1枚の表より、集計や確認がしやすくなります。
RecordとListを見分ける
| エディターの表示 | 意味 | JSONでの形 | 次にする操作 |
|---|---|---|---|
| Record | 項目名と値の組(1件分) | { } |
列として展開する |
| List | 値の並び | [ ] |
テーブルへの変換、新しい行に展開、値の抽出 |
| Table | すでに表になっている | ― | 展開する |
| 文字・数値 | 値そのもの | ― | そのまま使う |
考え方は単純です。Recordは横(列)に広げ、Listは縦(行)に広げるか1つのセルにまとめます。
手順1:JSONを読み込む
Excelの「データ」タブから「データの取得」→「ファイルから」→「JSONから」を選び、ファイルを指定します。メニューの名前や位置はExcelのバージョンで多少違うことがあります。
Web APIの応答を読む場合は、「データの取得」の「その他のデータソースから」→「Webから」でURLを指定します。認証方法や必要なパラメーターは、APIの提供元の仕様書で確認してください。
読み込むとPower Queryエディターが開きます。表示はJSONの最上位の形で変わります。
- 最上位がRecord(上の例):
ordersなどの項目名と、その値(List)が並ぶ - 最上位がList:Listの中身が並ぶ(多くの場合、1件ずつRecord)
手順2:表にしたい配列までたどる
上の例では、orders の行にある「List」の文字をクリックします。そのListの中身に移動するステップが追加され、Recordが縦に並んだ一覧になります。
最上位がListの場合、この手順はいりません。
手順3:Listをテーブルに変換する
「リスト ツール」の「変換」タブで「テーブルへの変換」を選びます。区切り記号は「なし」のまま、その他も既定のまま「OK」を押します。
1列(Column1)にRecordが並んだ表になります。
手順4:Recordの列を展開する
列見出しの右にある展開ボタン(左右を向いた矢印のアイコン)を押し、取り出す項目にチェックを入れます。
- 「元の列名をプレフィックスとして使用します」をオフにすると、
Column1.idのような列名にならない - 別の階層にも同じ名前の項目がある場合は、オンにして区別するか、あとで列名を変える
展開する項目の一覧は、先頭の一部の行だけから作られることがあります。「リストが不完全な可能性があります」と表示されたら、「さらに読み込む」を押してください。後ろの行にしかない項目も一覧に出てきます。
展開すると id・date・customer(Record)・items(List)の4列になります。
customer もRecordなので、同じように展開して code と name を取り出します。name のように意味があいまいな列名は、customer_name などに変えておくと後で混乱しません。
手順5:itemsのListをどう扱うか選ぶ
Listの列で展開ボタンを押すと、「新しい行に展開する」と「値の抽出」が表示されます。
新しい行に展開する
Listの要素ごとに行が分かれます。A001は2行になり、id・date・顧客の列は2行とも同じ値が繰り返されます。展開後の items 列にはRecordが入っているので、もう一度展開して sku と qty を取り出します。1明細1行の表にしたいときの方法です。
値の抽出
Listの中身を、区切り文字でつないで1つのセルにまとめます。中身が文字や数値だけのList(例:"tags": ["急ぎ", "再送"])なら、1注文1行のまま使えます。中身がRecordのListは、このままでは文字にできません。先に必要な項目を取り出す処理が必要です。
1注文1行の表にしたい場合は、items を展開せず、カスタム列で明細の件数だけ数えることもできます。
= List.Count([items])
明細の中身が必要なら、明細用のクエリを別に作ります。
行が増える箇所を確認する
行が増えるのは、Listを「新しい行に展開」したところだけです。展開の前と後で行数を確かめます。エディター下部のステータスバーに列数と行数が表示されます。
1注文1行の表の場合
行数と、id 列の重複しない値の数が一致するかを見ます。「表示」タブの「列のプロファイル」で個別の値の数を確認できます。既定では先頭1000行だけを対象に数えることがあるので、ステータスバーの「上位1000行に基づく列のプロファイル」をクリックし、データセット全体に切り替えて確認します。
1明細1行の表の場合
JSON内の明細数と行数が一致するかを確かめます。空のList("items": [])を持つ注文は、展開後に sku や qty がnullの行として残ることがあります。明細数と比べるときは、nullの行を除いて数えます。
2つ以上のListを同じ表で展開する場合
同じ表で2つのListを続けて「新しい行に展開」すると、組み合わせの数だけ行が増えます。たとえば明細2件とタグ3件の注文は6行になります。こうなる場合は、片方を「値の抽出」にするか、別のクエリに分けます。
nullの列が出たとき
nullは、原因によって対処が違います。
| 状況 | 考えられる原因 | 対処 |
|---|---|---|
| 一部の行だけnull | その件に項目がない、または値がnull | 元のJSONで該当する件を確認する。元からないなら空欄のままにするか、既定値に置き換える |
| 列全体がnull | 項目名の綴りや大文字・小文字が違う(id と ID)、または階層を間違えて展開した |
展開ボタンに出る項目名を確認し直す。Power Queryの項目名は大文字と小文字を区別する |
| 後ろの行にしかない項目が列にない | 展開時の項目一覧が一部の行から作られた | 展開ステップをやり直し、「さらに読み込む」で項目を追加する |
| 明細の列がnullの行がある | 空のListを展開した | 1明細1行の表なら除外するか、「明細なし」として残すかを決める |
データ型は最後に設定する
展開した直後の列は、型が決まっていない状態(列見出しのアイコンが「ABC123」)です。JSONの日付は文字列で入っていることが多いため、展開が終わってから日付や数値の列に型を設定します。
型の変換でエラーが出たら、元のデータの形式を確認します。2026-09-01 のような日付だけの形か、時刻やタイムゾーンが付いているかで、選ぶ型が変わります。
操作をMで見たときの形
ここまでの操作を詳細エディターで見ると、次のような形になります。画面で操作した場合、ステップ名は自動で付き、細かい書き方もこの例とは違うことがあります。
let
ソース = Json.Document(File.Contents("C:\data\orders.json")),
orders = ソース[orders],
テーブル = Table.FromList(orders, Splitter.SplitByNothing(), {"Column1"}),
注文展開 = Table.ExpandRecordColumn(テーブル, "Column1", {"id", "date", "customer", "items"}),
顧客展開 = Table.ExpandRecordColumn(注文展開, "customer", {"code", "name"}, {"customer_code", "customer_name"}),
明細行 = Table.ExpandListColumn(顧客展開, "items"),
明細展開 = Table.ExpandRecordColumn(明細行, "items", {"sku", "qty"}),
型設定 = Table.TransformColumnTypes(明細展開, {{"date", type date}, {"qty", Int64.Type}})
in
型設定
対応関係は次のとおりです。
Table.ExpandRecordColumn:Recordを列に展開するTable.ExpandListColumn:Listを新しい行に展開する
行が増えるのは Table.ExpandListColumn のステップだけ、と覚えておくと、どこで行数が変わったか追いやすくなります。
日本語が文字化けする場合は、Json.Document(File.Contents("パス"), 65001) のように2つ目の引数で文字コード(65001はUTF-8)を指定してみてください。
仕上げのチェックリスト
- 1行が何の1件か(注文か明細か)を決めた
- 行数が、決めた単位の件数と一致する
- Listを新しい行に展開したのは、必要な列だけになっている
- 空のListから生まれたnullの行の扱いを決めた
- 「さらに読み込む」で、後ろの行にしかない項目を確認した
- 列名が重複しておらず、意味が分かる
- 日付・数値の列に型を設定し、変換エラーがない
