Power QueryでJSONを取り込み、フラットな表に整形するMコード

Power Queryでは、Json.Document を用いてJSONを読み込み、レコードのリストを表に変換できます。ここでは、ローカルJSONと入れ子構造のJSONを扱う代表的なパターンをご紹介いたします。変数名やファイル名は本記事用に変更しております。


目次

1. ルートが「レコードのリスト」のJSONを読み込む例

JSON の先頭階層が配列で、各要素がレコードである一般的な形式を想定します。

let
    // JSONの読み込み
    SourceBinary = File.Contents("C:\Data\sales_data.json"),
    ParsedJson   = Json.Document(SourceBinary),

    // レコードのリストを表へ変換
    AsTable      = Table.FromRecords(ParsedJson),

    // 必要に応じて列を限定
    Selected     = Table.SelectColumns(AsTable, {"OrderId", "OrderDate", "Customer", "Amount"}),

    // 型を明示
    Typed        = Table.TransformColumnTypes(
        Selected,
        {
            {"OrderId", Int64.Type},
            {"OrderDate", type date},
            {"Customer", type text},
            {"Amount", type number}
        }
    )
in
    Typed

要点

  • Table.FromRecords は、レコードのリストをそのままテーブルにできます。
  • 並べ替えや集計を行う前に、Table.TransformColumnTypes で型を確定させると安定いたします。

2. ルートがオブジェクトで、中に配列があるJSONを読み込む例

多くのAPIでは、{ "items": [ ... ] } のように配列が入れ子になっています。

let
    // JSONの読み込み
    SourceBinary = File.Contents("C:\Data\orders_nested.json"),
    ParsedJson   = Json.Document(SourceBinary),

    // オブジェクト配下の items 配列を取得
    ItemsList    = ParsedJson[items],

    // レコードのリストを表へ
    AsTable      = Table.FromRecords(ItemsList),

    // 入れ子のレコード列がある場合は展開(例: customer 列をフラット化)
    Expanded     = if Table.HasColumns(AsTable, {"customer"})
                   then Table.ExpandRecordColumn(
                            AsTable,
                            "customer",
                            {"name", "id"},
                            {"customer.name", "customer.id"}
                        )
                   else AsTable,

    // 型を明示
    Typed        = Table.TransformColumnTypes(
        Expanded,
        {
            {"date", type datetime},
            {"amount", type number},
            {"customer.name", type text},
            {"customer.id", type text}
        }
    )
in
    Typed

要点

  • 入れ子の列は Table.ExpandRecordColumnTable.ExpandListColumn で段階的に展開いたします。
  • 実データの列名に合わせて {"name","id"} などの項目を調整してください。

3. Web上のJSONを直接取り込む例

認証不要のAPIや公開JSONであれば、Web.Contents と組み合わせて同様に扱えます。

let
    ApiBinary = Web.Contents("https://api.example.org/v1/public/orders?limit=100"),
    ApiJson   = Json.Document(ApiBinary),
    DataList  = ApiJson[data],
    AsTable   = Table.FromRecords(DataList),
    Typed     = Table.TransformColumnTypes(AsTable, {{"id", Int64.Type}, {"createdAt", type datetime}})
in
    Typed

注意点

  • 組織のデータ接続設定やプライバシーレベルにより、接続方法の指定が必要な場合がございます。
  • 文字コードの指定はCSVと異なり不要です。File.ContentsJson.Document の組み合わせで問題なく解釈されるのが一般的です。

4. 例外に強くするための簡易対策

列の存在有無や欠損を考慮し、try ... otherwise で安全に取り出すと、API変更時の耐性が向上します。

let
    SourceBinary = File.Contents("C:\Data\samples.json"),
    ParsedJson   = Json.Document(SourceBinary),
    ListSafe     = try ParsedJson otherwise {},
    AsTable      = Table.FromRecords(ListSafe),
    // 欠損列があっても落ちにくくする
    Cols         = {"id","date","value"},
    AddedCols    = List.Accumulate(
                      Cols,
                      AsTable,
                      (state, col) => if Table.HasColumns(state, {col})
                                      then state
                                      else Table.AddColumn(state, col, each null)
                   )
in
    AddedCols

まとめ

  • ルートが配列なら Table.FromRecords が最短経路です。
  • 入れ子構造は Table.ExpandRecordColumnTable.ExpandListColumn で段階的にフラット化します。
  • 型定義を早めに行うことで、後続処理の安定性が高まります。
  • APIやフィードの仕様変更に備え、列存在チェックや try ... otherwise を取り入れると保守性が向上いたします。
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

私が勉強したこと、実践したこと、してることを書いているブログです。
主に資産運用について書いていたのですが、
最近はプログラミングに興味があるので、今はそればっかりです。

目次