#はじめに
会社のITメンバーで参加している2019年のアドベントカレンダーに、自身2つ目の記事を投稿します!
自己紹介も兼ねて、前回書いた記事はこちらになります。
https://qiita.com/tom-k7/items/d8ef19dccb42891a0698
今回もOutsystemsについて書きます。
今はちょうどOutsystemsでCSV変換ツール(Webアプリ)を開発しているので、
OutsystemsでCSVの読み込み・出力の実装方法、あとは開発中にぶち当たった壁について共有させていただきます。
#やりたいこと
今開発しているツールでやりたいこととしては、
いろんな他社さんのサイトからダウンロードしたCSVファイルを
社内で使ってるシステムにインポートできるフォーマットのCSVファイルに変換したい。
なので、ダウンロードしたCSVファイルを読み込んで、
ごにょごにょ変換して新たなCSVファイルを出力するということをOutsystemsで実現します。
#Forge
CSVファイルの読み込み・出力にはCSVUtilのForgeコンポーネントを使います。
https://www.outsystems.com/forge/component-overview/636/csvutil
こちらをダウンロードし、ServiceStudioにインストールしてDependencyに追加してください。
#画面の開発
CSV処理をやる前に画面を作っちゃいます。
###CSVファイルのアップロード
まずは読み込む対象のCSVファイルをアップロードする必要があります。
方法は2つあります。
①Uploadを使う
②RichWidgets\Popup_Uploadを使う
今回は①のほうが良いということになったので、Uploadを使います。
※ちなみにUploadウィジェットは、ListRecordsのようなリストの中では動かないのでご注意ください。
###変換処理を行うサーバアクションを呼ぶためのボタンを配置
ConvertボタンのDestinationに変換処理を行うConvertサーバアクションを指定し、
そのサーバアクション内で変換処理を行います。
#CSV読み込み処理の実装
さて、ここからは本題のCSVの処理です。
まずはCSVファイルの読み込みを実装します。
###LoadConfigの設定
CSV読み込み時の設定をするためのCSVLoadConfig型のローカル変数を定義し、Assignで設定します。
主な設定内容は以下の通り。
- Encode:CSVファイルの文字コード。utf-8やshift-jisなどを設定。
- IsSkipHeader:CSVファイルにヘッダー行がある場合はTrue、ない場合はFalse。
- IsIgnoreColumnChange:
カラム数が合わない場合にエラーとする場合はFalse、エラーとしない場合はTrue。
試してみたら違いました。。このフィールド謎です"(-""-)" - FieldDelimiter:カラムの間の区切り文字。複数文字入れられるが、先頭の1文字しか適用されない。
- IsDisableDoubleQuote:カラムがダブルクオーテーションで囲われてる場合はFalse、そうでない場合はTrue。
###読み込み用RecordListを定義
CSVを読み込んだ結果を格納するEntity/StructureのRecordのリストを、ローカル変数で定義します。
今回は自作のSourceCSVというStructureのRecordListにしています。
※StructureのListでなく、StructureのRecordのListにしないとうまくいきません。
###Extension処理の呼出し
ForgeのCSVUtilで定義されているLoadCSVRecordListサーバアクションを呼び出し、
引数には画面で作ったUploadのContentと、CSVLoadConfigのローカル変数、
Entity/StructureのRecordListをToObject()で変換した結果をそれぞれ設定します。
これで読み込みは完了です。
#CSV出力処理の実装
続いて、CSV出力のほうも実装していきます。
###ExportConfigの設定
CSV出力時の設定をするためのCSVExportConfig型のローカル変数を定義し、Assignで設定します。
主な設定内容は以下の通り。
- IsShowHeader:CSVファイルにヘッダー行を入れる場合はTrue、入れない場合はFalse。
- FieldDelimiter:カラムの間の区切り文字。複数文字入れられるが、先頭の1文字しか適用されない。
- EncodeMode:カラムをダブルクオーテーションで囲うか否か。auto/quote/noquote/noquote_nocheckのいずれかを文字列で設定。
- LineSeparator:改行コード。CRだとChr(13)、LFだとChr(10)、CRLFはChr(13) + Chr(10)と設定。
###出力用RecordListを定義
出力するCSVのデータを格納するためのEntity/StructureのRecordのリストを、ローカル変数で定義します。
画像は自作のOutputCSVというStructureのRecordListにしています。
###出力用RecordListに読み込んだデータを変換して設定
↑で定義した出力用RecordListに対して、Loadしたデータが入っているRecordListの全件を設定します。
その際に値を変換したり、結合したり、不要なものは捨てたりして出力用に設定すれば変換ができますね。
画像ではListAppendAllで単純に項目の結合だけしてますが、実際にはここでいろんな変換をします。
かなり複雑なことをやる場合はForEachでループするケースもあると思います。
###Extension処理の呼出し
CSVUtilで定義されているExportRecordList2CSVサーバアクションを呼び出し、
引数にはEntity/StructureのRecordListをToObject()で変換した結果とCSVExportConfigのローカル変数をそれぞれ設定します。
###変換後CSVファイルの出力(ダウンロード)
最後にExportRecordList2CSVで作成されたCSVデータをDownloadウィジェットに渡して終了です。
#開発中に引っかかった罠
以上が実装方法になりますが、その他CSVUtilを使っててぶち当たった壁について共有したいと思います。
###出力時にダブルクオーテーションで囲う方法がわかりづらい
「ExportConfigの設定」でダブルクオーテーションで囲う方法はすでに記載済みですが、
そこにたどり着くまでに結構かかりました。
だって、その項目の名前EncodeModeて。。
文字コードのことかと思うやん。わからんて。。
ていう愚痴でしたww
ちなみにCSVExportConfig.EncodeModeに"quote"を指定するとダブルクオーテーションで囲ってくれます!
###Export処理でNullReferenceException
CSVUtilのExportRecordList2CSVサーバアクションを呼び出した際にこんなエラーが出ました。
Object reference not set to an instance of an object.
NullReferenceExceptionです。Javaでいうヌルポですね。
CSVの項目としては基本型しか使ってないので、Outsystemsで基本型でNullってあるんだっけ?って感じでした。
それでこのエラーはこれ以上詳細にはログに出ないので、いろいろ試してもうまくいかず途方に暮れた結果、
Extensionの中見るしかないな!ってなりました。
でExtensionダウンロードして、VisualStudioでsln開いてソース追っても原因わからず。
じゃあってことで、実際に渡してたデータをC#上で作って実行してみました!(^^)!(これが地味にめんどかった)
やってみた結果、渡したデータのカラムが1個足りてないせいでNullReferenceExceptionになっていたことが判明。
そのカラム何だろうと思ってたら、データ変換するときに特殊なことをしているカラムでした。
説明が意味不明だったらすいません
例えば読み込んだデータの値が"1"なら、変換後は"大学院"とするみたいなものをJSONファイルにKeyとValueのペアとして定義し、
JSONファイルをResourcesとしてモジュールにインポート。
そのJSONファイルをJSONDeserializeで読み込み、ForgeのHashTableを使ってKeyとValueのペアとしてメモリに格納しておき、
変換時はそのHashTableからgetした値を設定するという処理にしていました。
ForgeのHashable:https://www.outsystems.com/forge/component-overview/21/hashtable
それが、JSONで定義したKeyにはないデータ(↑のJSONだと"5"とか空文字とか)が読み込むCSVに入っていたため、getの結果がNullとなり、
それをExportRecordList2CSVに渡したためにNullReferenceExceptionとなったということでした。
Outsystemsで普通に実装してたらText型にNullは入らないのですが、
Extensionを介すとNullが入るんだなって知り、勉強になったなと思いました。
#最後に
OutsystemsでのCSV読み込み・出力について説明しました。
これからCSVUtilを使う方、同じ壁にぶつかった方の一助になればと思っています。