7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

インフォマティカ・ジャパン株式会社Advent Calendar 2024

Day 21

[CAIレシピ] CSVファイル読み込み・CSVファイル書き出し

Last updated at Posted at 2024-12-19

はじめに

この記事では、いつもよりも少し丁寧にCAIプロセスでCSVファイルを読み込む実装、CSVファイルを書き出す実装を確認します。

なお、この記事は次の記事の内容を理解していることを前提としています。

CSVファイル読み込み・CSVファイル書き出し

CAIプロセスの実装手順を確認する前に、実装方針について簡単に説明します。

  • CSVファイルの読み込み
    CAIでは、CDIのように任意のタイミングでCSVファイルを読み取って、少し変換して、CSVファイルに簡単に出力する手段が用意されていません。ではどのように実装するのかと言うと、XQueryの標準関数fn:unparsed-textfn:tokennizeを利用します。
    • fn:unparsed-text ... テキストファイル全体を文字列として読み込む
    • fn:tokenize ... 指定文字(改行コード・カンマ)で文字列を分割してシーケンス(配列)を取得する
  • CSVファイルへの書き出し
    CSVファイルへの書き出しも、やはりCDIのように簡単には設定できません。ヘッダー部はXMLで定義します。CSVのデータ部については動的にXMLを生成するためにXQueryによるコーディングが必要になります。

CAIプロセスの作成

最初に、今回CAIプロセスの動作確認をするために利用するCSVファイルを生成します。次のコマンドではカレントディレクトリに動作確認用のCSVファイルを生成しています。

cat << ___EOF___ > fruite.csv 
id, name, color
1, orange, orange
2, kiui, green
3, banana, yellow
4, mango, yellow
5, watermelon, red
6, melon, green
7, lemon, yellow
8, apple, red
9, grape, purple
___EOF___

次の手順では、CSVファイルを読み込んでテキストファイルの作成で定義したアプリケーション接続 recipe-appConn-FileWrite のイベント Delimited Content Writer を使ってCSVファイルを出力するCAIプロセスを作成しています。
読み込んだCSVファイルのid/name列を、File接続で定義したディレクトリに書き出します、もとのCSVファイルで定義しているcolor列は意図的に欠落させています

  1. CAIプロセスを次の設定で作成します。

    • 名前を recipe-psa-fileWriteCsv2Csv とする
    • 匿名アクセス を許可する
    • Secure Agent にデプロイする
  2. 一時フィールドとして tmpFile を定義します。定義された接続タイプ にはアプリケーション接続 recipe-appConn-FileWrite を指定、タイプ として Delimited Content Serialization Task を指定します。
    image.png

  3. 出力フィールドとして output を定義します。定義された接続タイプ にはアプリケーション接続 recipe-appConn-FileWrite を指定、タイプ として Delimited Content Serialization Result を指定します。
    image.png

  4. CAIプロセスが次の順に実行されるように各種ステップを追加します。
    image.png

  5. 割り当てステップを選択して、次のように設定します。
    image.png

    フィールド 割り当て
    tmpFile > File name 計算式 fn:tokenize(\$inputPath , '/')[last()]
    入力フィールドのファイルパスからファイル名を取得しています。
    tmpFile > Custom headers 計算式 (
    <Header>
    <fieldIndex>1</fieldIndex>
    <name>id</name>
    </Header>,
    <Header>
    <fieldIndex>2</fieldIndex>
    <name>name</name>
    </Header>
    )
    ヘッダーをXMLで指定しています。
    tmpFile > Custom objects 計算式 let \$text := fn:unparsed-text(\$input.inputPath )
    let \$lines := tokenize(\$text , '\n')
    let \$fieldnames:=tokenize(\$lines[1], '\s*,\s*')
    return
    for \$line at \$i in \$lines
    where \$i gt 1
    let \$fields := tokenize(\$line, '\s*,\s*')
    return
    <row>{
    for \$fieldname at \$j in \$fieldnames
    return
    element {fn:replace(\$fieldname,'"','')} {fn:replace(\$fields[\$j],'"','')}
    }</row>
    XQueryによるコーディングで、大きく次の2つの処理、『CSVファイルの読み込み』と『CSVファイル出力用のデータ生成』を行っています。コードについてはCSVデータ部を生成するXQueryコードの説明に補足を書いてあります。
  6. サービスステップを選択して サービスタイプ=接続接続=recipe-appConn-FileWriteアクション=DelimitedContentWriter を指定します。
    image.png

  7. サービスステップの 入力フィールド タブを選択して、次のように設定します。
    image.png

  8. 割り当てステップを選択して、出力フィールド output に イベント serializationResult( Delimited Content Writer の実行結果)を指定します。
    image.png

CAIプロセスの実行

curlコマンドを例とした動作確認結果です。ヘッダーの設定(Custom headers)にて、color列を含めていないため、出力ファイルにもcolor列が含まれない動作を確認できます。

curlコマンドと実行結果
curl -k https://localhost:7443/process-engine/public/rt/recipe-psa-fileWriteCsv2Csv \
-H 'Content-Type: application/json' \
-d '{"inputPath": "/opt/infaUSW5/ff/fruite.csv"}'

//実行結果
{
  "output": {
    "fileInfo": {
      "fullName": "fruite.csv",
      "name": "fruite",
      "ext": "csv",
      "path": "/opt/infaUSW5/ff/tgt/fruite.csv",
      "dir": "/opt/infaUSW5/ff/tgt",
      "size": 96,
      "lastModified": "2024-12-15T07:06:56Z"
    },
    "processedRecordsCount": 10,
    "writtenRecordsCount": 9,
    "success": true,
    "message": null
  }
}

//出力ファイルの確認
$ cat fruite.csv
id,name
1,orange
2,kiui
3,banana
4,mango
5,watermelon
6,melon
7,lemon
8,apple
9,grape

CSVデータ部を生成するXQueryコードの説明

次のXQueryコードについての説明です。

CSVデータ部を生成するXQueryコード
01: let $text := fn:unparsed-text($input.inputPath )
02: let $lines := tokenize($text , '\n')
03: let $fieldnames:=tokenize($lines[1], '\s*,\s*')
04: return
05:   for $line at $i in $lines
06:   where $i gt 1
07:   let $fields := tokenize($line, '\s*,\s*')
08:   return
09:   <row>{
10:     for $fieldname at $j in $fieldnames
11:     return
12:      element {$fieldname} {$fields[$j]}
13:   }</row>

  • 1行目: 入力フィールド inputPath で指定されたファイルのテキストデータを取得しています。
  • 2行目: 取得したテキストデータを改行コードで区切りシーケンス(配列)として取得しています。
  • 3行目: テキストデータの1行目($lines[1])、つまりヘッダー行をカンマで区切りシーケンス(配列)としてからフィールド名を取得しています。なお、ヘッダー行のフィールド名前後には空白が入っていることが想定されるため、tokenizeによる分割処理時には、正規表現 \s*,\s* を指定しています。
  • 5-6行目: テキストデータを改行コードで区切り、シーケンス(配列)の2行目以降(where $i gt 1)、つまりCSVファイルのデータ部を対象に処理します。
  • 9,13行目: 行データを示すrow要素の開始・終了を記述しています。
  • 10行目: 各フィールドを対象に処理します。この際、フィールド名に対応した位置(インデックス)を$jとして使えるようにfor ... at ... inという形式のfor文を使用しています。
  • 12行目: この処理は element {フィールド名} {フィールド値} の形式になっています。例えばelement {"example"} {"This is content"}<example>This is content</example>を返します。

なお、このXQueryでは次のようなXMLデータを生成しています。

<row>
  <id>1</id>
  <name>orange</name>
  <color>orange </color>
</row>
<row>
  <id>2</id>
  <name>kiui</name>
  <color>green </color>
</row>
...略

修飾子付きのCSVファイル形式に対応するには?

CSVファイルはシンプルにカンマで区切られている形式のほかに、列情報をダブルクォートで囲んでいる修飾子付きのCSVファイル形式もあります。今回の実装例では、前者のシンプルな形式のCSVを使っていましたが、後者の修飾子付きのCSVを扱う場合には、実装に工夫が必要です。

参考までの情報となりますが(CAIプロセスでは動作確認をしていませんが)、Javaでは正規表現,(?=(([^\"]*\"){2})*[^\"]*$)により文字列を分割して修飾子付きのCSVファイルの各列を取得できる動作を確認しています。

参照

7
0
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
7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?