Pythonを使ってIBM i でCSVファイルをJSONに変換する

# はじめに

以前の投稿で、IBM i 上でDBのデータをCSVに書き出す方法を書きました。  
最近では、JSON形式でのデータを求められる事も増えてきており、CSVから変換しているという話も聞いています。  
そこで、今回はCSVをJSONに変換する方法を紹介していきます。

CSVへの出力方法はこちらで書いています。  
https://qiita.com/tom_m_m/items/660cb6ce0bafaeac832f

本記事は、IBM i 7.5 環境を元に記載しております。  

# JSONへの変換方法

CSVファイルをJSONに変換する方法はいくつか考えられますが、Pythonで変換するのが簡単かと思います。  
IBM iでもPASE環境であれば、Pythonも実行できるので全てIBM i上で完結してJSONファイルの書き出しまで行うことができます。  

PASE環境へのアクセスは、以下になります。
- ACSからSSH端末を開く
- コマンドラインでsshアクセスする
- VSCodeなどを使ってアクセスする

どの方法でも大丈夫ですので、好きな方法で良いです。  

Pythonは、yumでインストール可能ですので、インストールが必要です。    

# 変換スクリプト

CSVファイルをJSONファイルに変換するだけであれば、とても簡単なスクリプトで実行できます。   
以下がサンプルです。  
`入力CSVファイル`と`出力JSONファイル`は、スクリプト実行時に指定できるようにしています。    
Pythonで操作するので、IFS上に入力ファイルをおいてください。  
出力ファイルもIFS上にかきだされます。  
ファイルは、IFS上のパスで指定します。  

`/home/user1/inputfile.csv` のような形式です。  

```python
import csv
import json
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]
print('入力CSVファイル: ', input_file)
print('出力JSONファイル: ', output_file)
 
# open CSV
with open(input_file, 'r', encoding='utf-8') as f:
    d_reader = csv.DictReader(f)
    d_list = [row for row in d_reader]
 
# export JSON
with open(output_file, 'w', encoding='utf-8') as f:
    json.dump(d_list, f, ensure_ascii=False)
```

前回の記事で書き出したCSVファイルを使って動作を確認してみます。  
書き出し済みのファイルはこちらです。  
```
demo # cat cpytoimpf_output.csv
"8010302","ｾﾝｼｬﾌﾞﾗｼ","洗車ブラシ","洗車ブラシ","2","1","1","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",260,189.00,238.00
"8010303","ﾂﾔﾀﾞｼﾗﾊﾞｰ","つや出しラバー","つや出しラバー","2","1","1","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",550,398.00,495.00
"8010304","ﾃﾝﾈﾝﾊﾁﾐﾂﾜｯｸｽ","天然蜂蜜ワックス","天然蜂蜜ワックス","2","1","1","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",900,765.00,880.00
"8010305","ﾜｯｸｽｽﾌﾟﾚｰﾀｲﾌﾟ","ワックススプレー","ワックススプレータイプ","2","1","1","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",400,288.00,360.00
"8020101","ﾍﾙﾒｯﾄ ﾌﾙ","ヘルメット  フル","ヘルメット  フル","2","2","1","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",6200,3840.00,4800.00
"8020102","ﾍﾙﾒｯﾄ","ヘルメット","ヘルメット","2","2","1","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",6000,3560.00,4450.00
"8020103","ｸﾞﾛｰﾌﾞ","グローブ","グローブ","2","2","1","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",4800,3040.00,3800.00
"13010101","ﾌｰﾄﾞｵｵﾌﾞｸﾛ","フード  大袋","フード  大袋","1","4","3","ﾊﾞﾗ","ﾎﾞｰﾙ","ｹｰｽ",700,585.00,630.00
```

使用するファイルを以下のようになっており、スクリプト`export_json.py`を使ってCSVファイル`cpytoimpf_output.csv`をJSONファイル`python_export_output.json`に変換します。  

```
demo
|-- cpytoimpf_output.csv
|-- export_json.py
```

では、実行してみます。  
```
demo # python export_json.py ./cpytoimpf_output.csv ./python_export_output.json
入力CSVファイル:  ./cpytoimpf_output.csv
出力JSONファイル:  ./python_export_output.json
```

出力が長いので途中までにしますが、書き出したファイルはこちらになります。  
ちゃんとJSON形式になっていますね。  
DBのカラム名がキーになっており、valueにデータが入っています。  
```
demo # cat python_export_output.json 
[{"CSHSHCD": "8010302", "CSHSHNK": "ｾﾝｼｬﾌﾞﾗｼ", "CSHSHNR": "洗車ブラシ", "CSHSHNM": "洗車ブラシ", "CSHHB1C": "2", "CSHHB2C": "1", "CSHHB3C": "1", "CSHTMN1": "ﾊﾞﾗ", "CSHTMN2": "ﾎﾞｰﾙ", "CSHTMN3": "ｹｰｽ", "CSHKUGP": "260", "CSHSPG1": "189.00", "CSHHPG1": "238.00"}, {"CSHSHCD": "8010303", "CSHSHNK": "ﾂﾔﾀﾞｼﾗﾊﾞｰ", "CSHSHNR": "つや出しラバー", "CSHSHNM": "つや出しラバー", "CSHHB1C": "2", "CSHHB2C": "1", "CSHHB3C": "1", "CSHTMN1": "ﾊﾞﾗ", "CSHTMN2": "ﾎﾞｰﾙ", "CSHTMN3": "ｹｰｽ", "CSHKUGP": "550", "CSHSPG1": "398.00", "CSHHPG1": "495.00"}, {"CSHSHCD": "8010304", "CSHSHNK": "ﾃﾝﾈﾝﾊﾁﾐﾂﾜｯｸｽ", "CSHSHNR": "天然蜂蜜ワックス", "CSHSHNM": "天然蜂蜜ワックス", "CSHHB1C": "2", "CSHHB2C": "1", "CSHHB3C": "1", "CSHTMN1": "ﾊﾞﾗ", "CSHTMN2": "ﾎﾞｰﾙ", "CSHTMN3": "ｹｰｽ", "CSHKUGP": "900", "CSHSPG1": "765.00", "CSHHPG1": "880.00"}, {"CSHSHCD": "8010305", "CSHSHNK": "ﾜｯｸｽｽﾌﾟﾚｰﾀｲﾌﾟ", "CSHSHNR": "ワ
```

JSONへの書き出しができているのはわかりますが、見づらいです。  
そこで、`jq`というコマンドが便利です。  
`jq`もyumでインストールできますので、使ってみてください。  
単純に中身を表示するだけであれば、このように使うと良いです。  
```
demo # cat python_export_output.json | jq .
[
  {
    "CSHSHCD": "8010302",
    "CSHSHNK": "ｾﾝｼｬﾌﾞﾗｼ",
    "CSHSHNR": "洗車ブラシ",
    "CSHSHNM": "洗車ブラシ",
    "CSHHB1C": "2",
    "CSHHB2C": "1",
    "CSHHB3C": "1",
    "CSHTMN1": "ﾊﾞﾗ",
    "CSHTMN2": "ﾎﾞｰﾙ",
    "CSHTMN3": "ｹｰｽ",
    "CSHKUGP": "260",
    "CSHSPG1": "189.00",
    "CSHHPG1": "238.00"
  },
  {
    "CSHSHCD": "8010303",
    "CSHSHNK": "ﾂﾔﾀﾞｼﾗﾊﾞｰ",
    "CSHSHNR": "つや出しラバー",
    "CSHSHNM": "つや出しラバー",
    "CSHHB1C": "2",
    "CSHHB2C": "1",
    "CSHHB3C": "1",
    "CSHTMN1": "ﾊﾞﾗ",
    "CSHTMN2": "ﾎﾞｰﾙ",
    "CSHTMN3": "ｹｰｽ",
    "CSHKUGP": "550",
    "CSHSPG1": "398.00",
    "CSHHPG1": "495.00"
```

使い方の一部ですが、フィルタリングなどもできます。  

```
demo # cat python_export_output.json | jq '.[] | select(.CSHSHCD == "8010303")'
{
  "CSHSHCD": "8010303",
  "CSHSHNK": "ﾂﾔﾀﾞｼﾗﾊﾞｰ",
  "CSHSHNR": "つや出しラバー",
  "CSHSHNM": "つや出しラバー",
  "CSHHB1C": "2",
  "CSHHB2C": "1",
  "CSHHB3C": "1",
  "CSHTMN1": "ﾊﾞﾗ",
  "CSHTMN2": "ﾎﾞｰﾙ",
  "CSHTMN3": "ｹｰｽ",
  "CSHKUGP": "550",
  "CSHSPG1": "398.00",
  "CSHHPG1": "495.00"
}
```
# Pythonで加工もできる

先程は、単純にすべてのデータをJSONに変換しました。  
ただ、一部のデータだけ書き出したいという要望もあるでしょう。  
Python内で、CSVのデータを配列に格納しているので加工もできますし、取り出すデータを指定することも可能です。  

例えば、スクリプトを以下のように変更してみます。  
```python
import csv
import json
import sys

input_file = sys.argv[1]
output_file = sys.argv[2]
print('入力CSVファイル: ', input_file)
print('出力JSONファイル: ', output_file)

# open CSV
with open(input_file, 'r', encoding='utf-8') as f:
    d_reader = csv.DictReader(f)
    rows = list(d_reader)

for i in range(len(rows)):
    rows[i] = {'CSHSHNM':rows[i]['CSHSHNM'], 'CSHTMN1':rows[i]['CSHTMN1'], 'CSHHPG1':rows[i]['CSHHPG1']}

## export JSON
with open(output_file, 'w', encoding='utf-8') as f:
    json.dump(rows, f, ensure_ascii=False)
```

こうすると、以下の部分で特定のカラムのみ取り出して書き出すことができます。  
```
for i in range(len(rows)):
    rows[i] = {'CSHSHNM':rows[i]['CSHSHNM'], 'CSHTMN1':rows[i]['CSHTMN1'], 'CSHHPG1':rows[i]['CSHHPG1']}
```

このスクリプトで出力したファイルは、このように特定のカラム情報のみを含んだものになります。  
```
demo # cat ext_out.json | jq .
[
  {
    "CSHSHNM": "洗車ブラシ",
    "CSHTMN1": "ﾊﾞﾗ",
    "CSHHPG1": "238.00"
  },
  {
    "CSHSHNM": "つや出しラバー",
    "CSHTMN1": "ﾊﾞﾗ",
    "CSHHPG1": "495.00"
  },
  {
```

# CLプログラムからも呼び出し可能  

CLプログラムから、PASEのコマンドを実行できることをご存知でしょうか？  
これができるので、CPYTOIMPFからPythonでのJSONファイルへの変換までCLプログラムで一気に実行できちゃいます。  

CLプログラムからは、こちらのコマンドでPythonを実行できます。  
Pythonの実行バイナリは、絶対パスで指定する必要があるので注意です。  
```
CALL QP2SHELL PARAM('/QOpenSys/pkgs/bin/python' 'Pythonプログラム')
```

CPYTOIMPFと一緒にCLプログラムとして書くと、このようになります。  

```
PGM

    CPYTOIMPF FROMFILE(DEMO/DEMOTABLE) TOSTMF('/home/demouser/demo/cl_cpytoimpf.csv') MBROPT(*REPLACE) STMFCCSID(01208) RCDDLM(*LF) STRDLM(*DBLQUOTE) RMVBLANK(*BOTH) ADDCOLNAM(*SQL)

    SBMJOB CMD(CALL PGM(QP2SHELL) + 
    PARM('/QOpenSys/pkgs/bin/python' + 
    '/home/demouser/demo/json_ext_out.py ' + 
    '/home/demouser/demo/cl_cpytoimpf.csv' + 
    '/home/demouser/demo/cl_json_out.json'))

ENDPGM
```

コンパイルして、実行するとCSVの書き出しからJSONへの変換まで一括して行われます。