#PowerAutomate でCSVデータをJSON化する

いろんなところでCSV形式のデータって公開されていたり、あるいはシステム間連携でCSV形式でデータを渡して、夜間に取り込みとかありますよね。
そういった定期処理であれば使えるかもしれない、CSVからJSONへの変換についてご紹介します。

※定期処理でJSON作ってどう使うかはあまり思いつかないですが、JSON形式にしてtxtファイルにしておけば、あとでstring to jsonですぐデータに戻せるから、データソースにもできるかもです。

# セットアップ
用意するCSVは以下のようなものです。

```input.txt
permalink,company,numEmps,category,city,state,fundedDate,raisedAmt,raisedCurrency,round
lifelock,LifeLock,,web,Tempe,AZ,1-May-07,6850000,USD,b
lifelock,LifeLock,,web,Tempe,AZ,1-Oct-06,6000000,USD,a
lifelock,LifeLock,,web,Tempe,AZ,1-Jan-08,25000000,USD,c
mycityfaces,MyCityFaces,7,web,Scottsdale,AZ,1-Jan-08,50000,USD,seed
```
テーブルで表すと

|permalink|company|numEmps|category|city|state|fundedDate|raisedAmt|raisedCurrency|round|
|------|-----|----|----|----|----|---|---|---|---|
|lifelock|LifeLock||web|Tempe|AZ|1-May-07|6850000|USD|b|
|lifelock|LifeLock||web|Tempe|AZ|1-Oct-06|6000000|USD|a|
|ifelock|LifeLock||web|Tempe|AZ|1-Jan-08|25000000|USD|c|
|mycityfaces|MyCityFaces|7|web|Scottsdale|AZ|1-Jan-08|50000|USD|seed|

これをPower Automateで加工して、以下のようなJSON Arrayを得ることを目指します。

```JSON:out.json
[
  {
    "permalink": "lifelock",
    "company": "LifeLock",
    "numEmps": "",
    "category": "web",
    "city": "Tempe",
    "state": "AZ",
    "fundedDate": "1-May-07",
    "raisedAmt": "6850000",
    "raisedCurrency": "USD",
    "round": "b"
  },
  {
    "permalink": "lifelock",
    "company": "LifeLock",
    "numEmps": "",
    "category": "web",
    "city": "Tempe",
    "state": "AZ",
    "fundedDate": "1-Oct-06",
    "raisedAmt": "6000000",
    "raisedCurrency": "USD",
    "round": "a"
  },
  {
    "permalink": "lifelock",
    "company": "LifeLock",
    "numEmps": "",
    "category": "web",
    "city": "Tempe",
    "state": "AZ",
    "fundedDate": "1-Jan-08",
    "raisedAmt": "25000000",
    "raisedCurrency": "USD",
    "round": "c"
  },
  {
    "permalink": "mycityfaces",
    "company": "MyCityFaces",
    "numEmps": "7",
    "category": "web",
    "city": "Scottsdale",
    "state": "AZ",
    "fundedDate": "1-Jan-08",
    "raisedAmt": "50000",
    "raisedCurrency": "USD",
    "round": "seed"
  },
..
]
```

これまではApply to eachをネストする方法が考えられていたのですが、あまり多重でループ処理したくないという思いから(エラー出たときに見にくい)、多重Apply to eachを回避した方法を紹介します。

# 方針・全体像
今回もSelectが大活躍するのと、加えて*Pieter's Method*を利用します。処理の各アクションは以下のようになります。
もともとただの文字列からjsonを作るので、どこかしらにjsonの形式にするアクションが必要ですが、今回でいえば一番最後の5.がそれにあたります。
![aaaa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/d82668eb-a0dc-488e-f63e-128659bc4540.png)

# 各アクション解説

## 1. 改行コードを定義
改行コードの定義はいろいろ方法があります。一番簡単なのはメモ帳とかで改行して、コピーペーストすればよいです。
もしくは、https://blog.tottokug.com/entry/2020/02/06/230353 こちらのブログか、あとは

```
decodeUriComponent('%0D%0A')　→ CRLF
decodeUriComponent('%0A')　→ LF
```
テキストの改行コードに応じて、上記の関数で分割ができます。Composeアクションで定義しておきましょう。

## 2. CSVを改行コードで分割
以降、便宜的に、CSVのデータをテーブルのようにあらわします。
ここでやるのは、まず行方向の分割です。改行コードを定義してあるので、その変数でCSVの内容を分割します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/77e32248-380a-7c40-71bd-93fc0310905f.png)

```Compose.action
split(body('Get_file_content'),outputs('Compose_2'))
```
※ComposeとCompose2の位置が逆になってしまったので、**改行コード**部分がoutputs('Compose_2')になっています

## 3. 行方向に分割した結果の0番目をヘッダー行として定義
最終的に生成するjsonのキー部分を先に作っておきます。
キーは、CSVのヘッダー行ですので、先ほど改行コードで分割した最初の行 (0番目) を、今度は横に分割します。 (カンマ区切り)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/3c2cb6ef-50cb-095d-8674-f002b8f34f6b.png)

## 4. 2行目～最終行までをループ処理
2行目～最終行までのループ処理ですが、雰囲気としては for(i=2; i<=N-1; i++) みたいな感じです。
配列としては2行目は、[1]になることに注意して、Apply to eachの最初の入力を、

```
range(1,sub(length(outputs('Compose')),1))
```
とします。*range()*は[以前の投稿](https://qiita.com/h-nagao/items/e0153b9a2c700cc2c95e)でも紹介しましたが、2つの入力がある関数で、range(N,M)の形式で、NからM個の整数配列を作ります。
ここでいえば、range(1,sub())なので、 **[1,2,3....(改行で分割した行数)-1]** が範囲になります。

## 5. Selectアクションで一行を処理
ここが一番のポイントです。Selectアクションで先に定義しておいたヘッダー行と、ループ対象行をカンマでsplitした結果から、jsonの素を作ります。図で書くと以下のような操作です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/873e62df-e3ad-111a-59ba-4ebf9d9c4b99.png)
このような処理は、[前の投稿(LookUp番外編)](https://qiita.com/h-nagao/items/25fb21d7133e25c07a34)でも紹介しています。

今回は、Selectの入力を、まずrange関数で[0,1,2,...,列数-1]にします。
この整数をもとにして、ヘッダー行をカンマで区切ったN列目と、ループ対象行をカンマで区切ったN列目を文字列結合します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/98a4b936-004d-1a0d-ac45-83268504fd33.png)
使った数式は

```
concat('"',outputs('Header')?[item()],'":"',split(outputs('Compose')?[items('Apply_to_each')],',')?[item()],'"')
```
作るのもとの対応としては・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/f362a320-2308-9181-fd81-88a823921250.png)
このような感じです。
これを実行した結果を次のステップでComposeしてjsonとしての1行にします。

## 6. ComposeでSelect結果をjson化
ここで*Pieter's Method*を利用します。
詳細： https://sharepains.com/2019/07/09/compose-actions-inside-apply-to-each-steps-in-microsoft-flow/

Apply to eachの中でComposeした結果を、Apply to eachの外側でComposeすると、なんと自動的に配列にしてくれるんです！！！！
すごい！！
これを実行するために、まずjsonオブジェクトを1つ作ります。

```
json(concat('{',join(body('Select'),','),'}'))
```
Selectの結果をカンマでjoinして、concatで前後に{}をつけます。これをjson関数でjsonに。
これによって生成されるのが、

```composed.json
{
 "company":"LifeLock",
 "category":"web",
 "city":"Tempe",
 ...
}
```
こんなjsonです。
あとはPieter's Methodでこれを配列にすれば完成！

# 完成！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/202779/2c74fa43-fcc8-ce12-3ee2-688ae47101f8.png)

サンプルを以下に配置しました。
https://github.com/mofumofu-dance/PowerApps365/blob/master/Samples/ConvertCSVtoJSONFlow_20200226031717.zip