3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

Power Automateを利用しているときに、
「このJSONのキーだけ抽出したいな」というシーンが良くあります。

キー、バリューで構成されるJSONですが、キー値がサクっと取り出せる方法がわかりませんでした。
JavaScriptでは、Object.keys()メソッドがあり、Office Scriptsに渡せば実現できますが、
そのためだけにOffice Scriptsを起動するのも気が乗りません。

可能であれば、Office Scriptsを起動せず、データ操作関数の範囲内で完了させたいところです。
そのような中、下記の記事を見つけ、課題が解決できたことから、コチラの紹介と応用技をシェアしたいと思います。

xml 関数xpath 関数のコラボレーション

今回学んだ手法は

  • キー値を取得したいJSONxml 関数XMLに変換する
  • xpath 関数XPathクエリを実行する

この二段階で実行することができます。

サンプルのJSONで試す

例えば下記のようなJSONがあったとします。

縦長なので、折りたたみます

samples
サンプルデータ
{
    "values": [
        {
            "productID": 1,
            "productName": "ノートパソコン",
            "category": "電子機器",
            "price": 120000,
            "stockQuantity": 50,
            "supplier": {
                "supplierID": 101,
                "supplierName": "株式会社テックサプライ"
            }
        },
        {
            "productID": 2,
            "productName": "デジタルカメラ",
            "category": "電子機器",
            "price": 85000,
            "stockQuantity": 30,
            "supplier": {
                "supplierID": 102,
                "supplierName": "カメラワールド株式会社"
            }
        },
        {
            "productID": 3,
            "productName": "エアフライヤー",
            "category": "家電",
            "price": 15000,
            "stockQuantity": 100,
            "supplier": {
                "supplierID": 103,
                "supplierName": "ホームアプライアンス株式会社"
            }
        },
        {
            "productID": 4,
            "productName": "ランニングシューズ",
            "category": "スポーツ用品",
            "price": 12000,
            "stockQuantity": 200,
            "supplier": {
                "supplierID": 104,
                "supplierName": "アクティブギア株式会社"
            }
        },
        {
            "productID": 5,
            "productName": "LEDテレビ",
            "category": "電子機器",
            "price": 50000,
            "stockQuantity": 40,
            "supplier": {
                "supplierID": 105,
                "supplierName": "ビジョンエレクトロニクス株式会社"
            }
        }
    ]
}

これをそのまま作成(Compose)でオブジェクトを設け、xml 関数を使ってみましょう。

Error
アクション 'XML_へ加工' に失敗しました: Unable to process template language expressions in action 'XML_へ加工' inputs at line '0' and column '0': 'The template language function 'xml' parameter is not valid. The provided value cannot be converted to XML: 'This document already has a 'DocumentElement' node.'. Please see https://aka.ms/logicexpressions#xml for usage details.'.

エラーが発生してしまいます。

なぜでしょうか。試しに親の階層を増やして、実行してみましょう。

{
  "root": @{outputs('Sample_JSON')}
}

image.png

こちらを実行すると下記の通りXMLが評価されます。
なるほど。rootを追加しない場合、valuesが複数にまたがってしまうため、最上位の親がわからなくなってしまうからのエラーなのでしょうね。

JSONから変換したXML
<root>
    <values>
        <productID>1</productID>
        <productName>ノートパソコン</productName>
        <category>電子機器</category>
        <price>120000</price>
        <stockQuantity>50</stockQuantity>
        <supplier>
            <supplierID>101</supplierID>
            <supplierName>株式会社テックサプライ</supplierName>
        </supplier>
    </values>
    <values>
        <productID>2</productID>
        <productName>デジタルカメラ</productName>
        <category>電子機器</category>
        <price>85000</price>
        <stockQuantity>30</stockQuantity>
        <supplier>
            <supplierID>102</supplierID>
            <supplierName>カメラワールド株式会社</supplierName>
        </supplier>
    </values>
    <values>
        <productID>3</productID>
        <productName>エアフライヤー</productName>
        <category>家電</category>
        <price>15000</price>
        <stockQuantity>100</stockQuantity>
        <supplier>
            <supplierID>103</supplierID>
            <supplierName>ホームアプライアンス株式会社</supplierName>
        </supplier>
    </values>
    <values>
        <productID>4</productID>
        <productName>ランニングシューズ</productName>
        <category>スポーツ用品</category>
        <price>12000</price>
        <stockQuantity>200</stockQuantity>
        <supplier>
            <supplierID>104</supplierID>
            <supplierName>アクティブギア株式会社</supplierName>
        </supplier>
    </values>
    <values>
        <productID>5</productID>
        <productName>LEDテレビ</productName>
        <category>電子機器</category>
        <price>50000</price>
        <stockQuantity>40</stockQuantity>
        <supplier>
            <supplierID>105</supplierID>
            <supplierName>ビジョンエレクトロニクス株式会社</supplierName>
        </supplier>
    </values>
</root>

これから、下記のxpath 関数を実行してみましょう。

xpath関数 でroot以下を取得
xpath(outputs('XML_へ加工'), '/root/*')

実行するとroot以下の要素が、Base64エンコードされた状態で返ってきます。

Base64エンコードされた結果が返ってきます
xpath関数の結果

[
    {
        "$content-type": "application/xml;charset=utf-8",
        "$content": "PHZhbHVlcz4NCiAgPHByb2R1Y3RJRD4xPC9wcm9kdWN0SUQ+DQogIDxwcm9kdWN0TmFtZT7jg47jg7zjg4jjg5Hjgr3jgrPjg7M8L3Byb2R1Y3ROYW1lPg0KICA8Y2F0ZWdvcnk+6Zu75a2Q5qmf5ZmoPC9jYXRlZ29yeT4NCiAgPHByaWNlPjEyMDAwMDwvcHJpY2U+DQogIDxzdG9ja1F1YW50aXR5PjUwPC9zdG9ja1F1YW50aXR5Pg0KICA8c3VwcGxpZXI+DQogICAgPHN1cHBsaWVySUQ+MTAxPC9zdXBwbGllcklEPg0KICAgIDxzdXBwbGllck5hbWU+5qCq5byP5Lya56S+44OG44OD44Kv44K144OX44Op44KkPC9zdXBwbGllck5hbWU+DQogIDwvc3VwcGxpZXI+DQo8L3ZhbHVlcz4="
    },
    {
        "$content-type": "application/xml;charset=utf-8",
        "$content": "PHZhbHVlcz4NCiAgPHByb2R1Y3RJRD4yPC9wcm9kdWN0SUQ+DQogIDxwcm9kdWN0TmFtZT7jg4fjgrjjgr/jg6vjgqvjg6Hjg6k8L3Byb2R1Y3ROYW1lPg0KICA8Y2F0ZWdvcnk+6Zu75a2Q5qmf5ZmoPC9jYXRlZ29yeT4NCiAgPHByaWNlPjg1MDAwPC9wcmljZT4NCiAgPHN0b2NrUXVhbnRpdHk+MzA8L3N0b2NrUXVhbnRpdHk+DQogIDxzdXBwbGllcj4NCiAgICA8c3VwcGxpZXJJRD4xMDI8L3N1cHBsaWVySUQ+DQogICAgPHN1cHBsaWVyTmFtZT7jgqvjg6Hjg6njg6/jg7zjg6vjg4nmoKrlvI/kvJrnpL48L3N1cHBsaWVyTmFtZT4NCiAgPC9zdXBwbGllcj4NCjwvdmFsdWVzPg=="
    },
    {
        "$content-type": "application/xml;charset=utf-8",
        "$content": "PHZhbHVlcz4NCiAgPHByb2R1Y3RJRD4zPC9wcm9kdWN0SUQ+DQogIDxwcm9kdWN0TmFtZT7jgqjjgqLjg5Xjg6njgqTjg6Tjg7w8L3Byb2R1Y3ROYW1lPg0KICA8Y2F0ZWdvcnk+5a626Zu7PC9jYXRlZ29yeT4NCiAgPHByaWNlPjE1MDAwPC9wcmljZT4NCiAgPHN0b2NrUXVhbnRpdHk+MTAwPC9zdG9ja1F1YW50aXR5Pg0KICA8c3VwcGxpZXI+DQogICAgPHN1cHBsaWVySUQ+MTAzPC9zdXBwbGllcklEPg0KICAgIDxzdXBwbGllck5hbWU+44Ob44O844Og44Ki44OX44Op44Kk44Ki44Oz44K55qCq5byP5Lya56S+PC9zdXBwbGllck5hbWU+DQogIDwvc3VwcGxpZXI+DQo8L3ZhbHVlcz4="
    },
    {
        "$content-type": "application/xml;charset=utf-8",
        "$content": "PHZhbHVlcz4NCiAgPHByb2R1Y3RJRD40PC9wcm9kdWN0SUQ+DQogIDxwcm9kdWN0TmFtZT7jg6njg7Pjg4vjg7PjgrDjgrfjg6Xjg7zjgro8L3Byb2R1Y3ROYW1lPg0KICA8Y2F0ZWdvcnk+44K544Od44O844OE55So5ZOBPC9jYXRlZ29yeT4NCiAgPHByaWNlPjEyMDAwPC9wcmljZT4NCiAgPHN0b2NrUXVhbnRpdHk+MjAwPC9zdG9ja1F1YW50aXR5Pg0KICA8c3VwcGxpZXI+DQogICAgPHN1cHBsaWVySUQ+MTA0PC9zdXBwbGllcklEPg0KICAgIDxzdXBwbGllck5hbWU+44Ki44Kv44OG44Kj44OW44Ku44Ki5qCq5byP5Lya56S+PC9zdXBwbGllck5hbWU+DQogIDwvc3VwcGxpZXI+DQo8L3ZhbHVlcz4="
    },
    {
        "$content-type": "application/xml;charset=utf-8",
        "$content": "PHZhbHVlcz4NCiAgPHByb2R1Y3RJRD41PC9wcm9kdWN0SUQ+DQogIDxwcm9kdWN0TmFtZT5MRUTjg4bjg6zjg5M8L3Byb2R1Y3ROYW1lPg0KICA8Y2F0ZWdvcnk+6Zu75a2Q5qmf5ZmoPC9jYXRlZ29yeT4NCiAgPHByaWNlPjUwMDAwPC9wcmljZT4NCiAgPHN0b2NrUXVhbnRpdHk+NDA8L3N0b2NrUXVhbnRpdHk+DQogIDxzdXBwbGllcj4NCiAgICA8c3VwcGxpZXJJRD4xMDU8L3N1cHBsaWVySUQ+DQogICAgPHN1cHBsaWVyTmFtZT7jg5Pjgrjjg6fjg7Pjgqjjg6zjgq/jg4jjg63jg4vjgq/jgrnmoKrlvI/kvJrnpL48L3N1cHBsaWVyTmFtZT4NCiAgPC9zdXBwbGllcj4NCjwvdmFsdWVzPg=="
    }
]

それぞれ下記のような形式で要素が抜き出されます。

ひとつめの要素
<values>
  <productID>1</productID>
  <productName>ノートパソコン</productName>
  <category>電子機器</category>
  <price>120000</price>
  <stockQuantity>50</stockQuantity>
  <supplier>
    <supplierID>101</supplierID>
    <supplierName>株式会社テックサプライ</supplierName>
  </supplier>
</values>

ここから更に選択(Select)アクションを用いて、各要素にxpath 関数を実行します。

From
@{outputs('xpath関数_でroot以下を取得')}
{
 "key" : @{xpath(item(),'name(/*)')}
}

image.png

xpath(item(),'name(/*)')のうち、'name(/*)'は、ルート要素を取得するためのXPath式です。
実行すると各エントリのroot要素が取得できます。

[
  {
    "key": "values"
  },
  {
    "key": "values"
  },
  {
    "key": "values"
  },
  {
    "key": "values"
  },
  {
    "key": "values"
  }
]

ほー。これで親要素、言わばカラム名が取得できるのですね!光明が見えてきました。


JSON配列入力の列名を取得する

ここからが応用編です。

今回は各配列に単一の親を持たせるために、Root の追加で、rootを明示的に追加しましたが、JSON配列入力の最初の要素から、xml 関数を実行する形式に変えてみましょう。

Root の追加
- outputs('Sample_JSON')
+ first(outputs('Sample_JSON')?['values'])

コチラを実行すると、文字通り最初のエントリからrootの下に、カラム名に該当する要素が列挙されます。

xml関数の結果
<root>
    <productID>1</productID>
    <productName>ノートパソコン</productName>
    <category>電子機器</category>
    <price>120000</price>
    <stockQuantity>50</stockQuantity>
    <supplier>
        <supplierID>101</supplierID>
        <supplierName>株式会社テックサプライ</supplierName>
    </supplier>
</root>

コチラで前述の

  • xpath(outputs('XML_へ加工'), '/root/*')
  • 選択(Select)
    • 各要素にxpath(item(),'name(/*)')

上記を実行することで、選択(Select)アクションの戻り値に、カラム名を列挙することができました🙌

[
  {
    "key": "productID"
  },
  {
    "key": "productName"
  },
  {
    "key": "category"
  },
  {
    "key": "price"
  },
  {
    "key": "stockQuantity"
  },
  {
    "key": "supplier"
  }
]

可変のJSONに対応できる

これができると嬉しいことは可変のJSON配列入力に対応できることです。
親子関係が崩れると適用できませんが、そちらが担保されている場合、カラムが増えても対処できるようになります。

例えばGPTランダムなJSON配列入力を出してもらうといったシーンのあと、テーブル作成に活かすことができます。

プロンプトの例は下記の記事をご覧ください。

度々歯がゆい思いをしていた、Markdownのテーブルのヘッダー、区切り文字も含めて、
Markdownのテーブルの作成をしてみたいと思います。

シナリオ

  1. GPT-4oを用いてダミーデータを作成する
  2. 戻り値のJSON配列入力を解析する
  3. カラムを特定する
  4. ヘッダーと区切り文字、本文をMarkdownに加工する
  5. 承認コネクタで表示する

承認コネクタを利用している理由は、Markdownがサポートされていることからです。

今回は 2 から取り組みます。

2. 戻り値のJSON配列入力を解析する

子フローに分ける場合は、こちらからがよろしいかと

JSON の解析を実行するのみですが、Schemaの入力を{}のみにします。

image.png

これにより、オブジェクトとして解析はされますが、JSON Schemaの構造変化に対応します。

3. カラムを特定する

  1. root属性をJSON配列入力に追加
  2. xml 関数XMLへ加工
  3. xpath 関数root以下の要素を取得
  4. xpath 関数でカラム名を取得

今回解説した内容になります。

挙動の詳細

1.root属性をJSON配列入力に追加

image.png

{
  "root": @{first(body('Parse_JSON')?['values'])}
}

2.xml 関数XMLへ加工

@{xml(outputs('Root_の追加'))}

3.xpath 関数root以下の要素を取得

@{xpath(outputs('XML_へ加工'), '/root/*')}

4.xpath 関数でカラム名を取得

@{xpath(outputs('XML_へ加工'), '/root/*')}

このアクションでカラム名を取得します。

4. ヘッダーと区切り文字、本文をMarkdownに加工する

ヘッダーと区切り文字

まずは結果を格納する配列を変数を初期化するで用意します。

image.png

データ型はArrayです。

ヘッダー本文で処理を分岐します。

image.png

ヘッダーは、各要素を結合するのみです。

image.png

From
@{body('Select_Header')}
Join With
|

結合(Join)アクションを|で実施します。

次にヘッダーの要素数に用いて---を挿入します。
今回は選択(Select)アクションを用いて、---を配列に格納しました。

image.png

From
@{body('Select_Header')}
Join With
@{string('---')}

上記の---を格納した配列も、結合(Join)アクションを|で実施します。

image.png

From
@{body('Header区切り_配列')}
Join With
|

本文

Apply to eachを使用します。

image.png

JSON の解析で取得した配列に該当するvaluesで繰り返し処理を実行します。

@{body('Parse_JSON')?['values']}

各行の値は、選択(Select)アクションをテクニカルに使って配列にします。

image.png

From
@{body('Select_Header')}
Map
@{items('Apply_to_each')[item()]}

この部分は、下記のブログが参考になりますので、おすすめいたします。

あとは各要素を|結合(Join)し、配列変数に値を追加していきます。

image.png

From
@{body('行_一次元配列')}
Join With
|

上記の結果を随時Arrayに格納。
image.png

Apply to eachが完了次第、改行結合(Join)を実施します。

image.png

From
@{variables('Array')}
Join With


承認コネクタで表示する

作成(Compose)アクションでMarkdownをまとめます。

image.png

入力
|@{body('Header_結合')}|
|@{body('Header区切り_結合')}|
|@{body('各要素を改行して結合')}|

承認コネクタはカスタム応答で、このようにやってみましょう。

image.png

結果は・・・!

GPTからの返答になるので、ムラもありますが、可変の出力に対応して、Markdownのテーブルが作成できました!

■ Teams
image.png

見切れていますね

■ Outlook
image.png

列の要素や文字列が多すぎると見切れます。

■ Power Automateモバイル
image.png

まだまともですね。

とりあえずできました!
やったぜ!

おわりに

頭をコネコネするいい体操になりましたね。
テーブル作成は、要件変更の場合、影響範囲の調査が手間なので使っていきたいです。

改めて知見を広めてくださる方に感謝!
読んでくださる皆様にも感謝です🐟

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?