RPG で XML 文書を処理する (XML 要素の繰り返し件数が事前に決められていない場合)

ここまで見てきた「RPG で XML 文書を処理する (その 1)」と「RPG で XML 文書を処理する (その 2)」では、XML ファイルの繰り返し要素の数は、あらかじめ件数がわかっているか、準備してある配列分に収まる件数に限られる、というのが前提でした。

実際に XML ファイルを受け取ってデータ処理をする場合、要素数が決められている場合もあるでしょうし、受け取って読み取るまでまではわからない、という場合もあるでしょう。
たとえば、発注などに使われているような場合であれば、項目数は実際に読み取ってみるまでわかりませんよね。

今回は XML 文書に存在する要素の繰り返し数がわからない、つまり XML ファイルで提供されるデータの件数はあらかじめわからない、という状況の場合にどうするか、を見ていきたいと思います。


データを書き込むファイル (テーブル)

PARTS というファイルがあり、これに XML データを読んだ結果を書き込むこととします。

*************** データの始め **************** 
     A          R PARTREC                     
     A            ID            10P 0         
     A            QTY           10P 0         
     A            COST           7P 2         
***************** データの終わり ************  

データを読み込む元の XML ファイル

parts.xml という XML ファイルでデータは提供されることとします。
XML 要素の名前と物理ファイルのフィールド名は今回同じになっていますが、RPG のコーディング例を見るとわかるとおり、必ずしも同じである必要はありません。(データ・ストラクチャーのサブフィールド名が同じであればいいので、いったん受け取ってからファイルの各フィールドにセットしてやればいいわけですね)

<parts> 
 <part><qty>100</qty><id>13</id><cost>12.03</cost></part> 
  <part><qty>9</qty><id>14</id><cost>3.50</cost></part> 
  <part><qty>9</qty><id>15</id><cost>3.50</cost></part> 
  <part><qty>9</qty><id>24</id><cost>3.50</cost></part> 
  <part><qty>9</qty><id>34</id><cost>3.50</cost></part> 
   <part><qty>9</qty><id>54</id><cost>3.50</cost></part> 
  <part><qty>9</qty><id>74</id><cost>3.50</cost></part> 
  <part><qty>9</qty><id>11</id><cost>3.50</cost></part> 
  <part><qty>9</qty><id>22</id><cost>3.50</cost></part> 
  <part><qty>9</qty><id>124</id><cost>3.50</cost></part> 
 <part><qty>0</qty><id>254</id><cost>1.98</cost></part> 
</parts>

XML ふぁいるは iSeries ナビゲーターの「ファイル・システム」経由で IFS に転送したので、CCSID は 943 になっています。

コーディング例 (XML-INTO と %HANDLER)

こちらがサンプルのコーディングです。
基本的にはインフォメーション・センターに載っていたサンプル (Figure 384. Parsing an unknown number of XML elements using a handling procedure) を大半流用しています。 (少なくとも現時点ではそのままでは動かなかったのでいろいろ変更していますが ...... )

ファイルは一応読み込みもできるようにオープンしていますが、(出力しかしないのであれば) 以下のように出力用でオープンしてもいいでしょう。

FParts     O    e             disk    prefix(pr_) 
*************** データの始め ****************                           
H DFTACTGRP(*NO)                                                       
FParts     if a e             disk    prefix(pr_)                       
D Part          e ds                  extname(parts) qualified         
 *                                                                     
D file            S             50A                                     
D options         S             50A                                     
D allOk           S               N                                     
 *                                                                     
D                                                                       
 *                                                                     
D partHdlr        PR            10I 0                                   
D   ok                            N                                     
D   parts                             CONST LIKEDS(part)               
D                                     DIM(3)                           
D   numRecs                     10I 0 VALUE                             
 *                                                                     
 /Free                                                                 
                                                                       
  file = '/home/XML/parts.xml';                                   
  options = 'doc=file path=parts/part case=any';                       
  allOk = *ON;                                                         
  xml-into %HANDLER(partHdlr : allOk)                                 
           %XML(%trimr(file) : %trimr(options));                       
                                                                       
   if not allOk;                                                       
      // some output error occurred                                   
   endif;                                                             
                                                                       
   *inLR = *ON ;                                                       
   return ;                                                           
 /end-free                                                             
 *   
     // 二番目の引数の DIM 回数毎に呼び出される                                                                  
P partHdlr        B                                                     
D partHdlr        PI            10I 0                                   
D   ok                            N                                     
D   parts                             CONST LIKEDS(part)                 
D                                     DIM(3)     
    // 受け取りデータ構造の数 (最後の時以外は 3・最後も 3以下)                         
D   numRecs                     10I 0 VALUE                             
 *                                                                       
D i               S             10I 0                                   
 *                                                                       
D xmlRecNum       S             10I 0 STATIC INZ(0)                     
 /free  
      // プロシージャにわたされた件数分のループ                                                                 
  for i = 1 to numRecs;         
      //レコード総件数カウント(グローバル変数)を累計カウント                                         
     xmlRecNum = xmlRecNum + 1;                                         
     pr_id = parts(i).id ;                                             
     pr_qty = parts(i).qty ;                                           
     pr_cost = parts(i).cost ;                                         
     write(e) partRec ;                                                 
     if %error;                                                       
       // log information about the error                             
       // logOutputError (xmlRecNum : parts(i));     
       // エラーになったレコードがあったという事を示す                  
       ok = *OFF;                                                     
     endif;                                                           
  endfor;                                                             
                                                                       
  return 0;                                                           
 /end-free                                                             
P                 E                                                     
***************** データの終わり *************

ファイル名と XML 処理のオプションをセットしています。

file = '/home/XML/parts.xml';
options = 'doc=file path=parts/part case=any';

一件でもエラーがあった、ということを示すフラグとして allOk をセットしています。

allOk = *ON;

xml-into 命令の最初の引数に、%HANDLER にプロシージャ名とその引数をセットします。
また、ファイル名やオプションを変数で指定する場合は %trimr を使用して右側のブランクを削除しておきます。

xml-into %HANDLER(partHdlr : allOk) %XML(%trimr(file) : %trimr(options));

partHdlr というサブプロシージャは、最後に P 仕様書で定義されています。

%HANDLER で指定できるサブプロシージャについて、戻り値は 4バイトの integer、引数は 3つ、と決まっています。

最初の引数 (今回は ok) についてはアプリケーションの要件に応じてどんなデータ型を使ってもかまいません。今回はエラーの確認のためのフラグに使用しています。
二番目の引数 (今回は parts) で XML 要素を読み取り専用で受け取るデータ構造の配列を指定します。(つまり DIM(n) と CONST キーワードは必須ですね) XML 要素はこの配列要素の数に分割されてこのプロシージャに渡されます。たとえば、繰り返し要素/データ件数が 10 件あったとすると、ここで 3個の配列としている場合は 3件ずつ 3回と最後に 1件のデータ構造のみを渡された 1回と計 4回このプロシージャが呼び出されることになります。
三番目の引数は、このプロシージャが呼び出された時に渡されているデータ構造の要素の数になります。つまり前の行で述べた例でいくと最初から 3回目までは 3、最後の1回は 1 という数字が入ってきます。この引数は値渡しなので VALUE キーワードを指定します。

P partHdlr        B                                                     
D partHdlr        PI            10I 0                                   
D   ok                            N                                     
D   parts                             CONST LIKEDS(part)                 
D                                     DIM(3)     
D   numRecs                     10I 0 VALUE     

三番目の引数 (渡された配列要素数) 分だけ for でループして処理すれば、結果的に全件処理することになります。

STATIC キーワードを指定してグローバル変数とした xmlRecNum フィールドで累積の処理件数をカウントしています。

データ・ストラクチャーの配列のサブフィールドと要素の指定の仕方はわかりますよね。

  for i = 1 to numRecs;         
     xmlRecNum = xmlRecNum + 1;                                         
     pr_id = parts(i).id ;                                             
     pr_qty = parts(i).qty ;                                           
     pr_cost = parts(i).cost ;                                         
     write(e) partRec ;    

ちなみに今回はちゃんと何回もプロシージャが呼び出されることを確認したくてデータ・ストラクチャーの配列数を 3つなどという小さい数にしているので、実際にはもっともっと大きい値を指定する方が現実的です。
プロシージャ呼び出しのオーバーヘッドはそれほどないとは思いますが、何回も呼び出すのはあまり効率的な処理とは言えませんので ......

実行結果

実行結果は、こんなかんじです。
ちゃんと要素毎にレコードになっているのが確認できます。

CCSID 819 の場合

バイナリーモードで FTP すると CCSID は 819 になります。
この CCSID でどうなるかも実行してみました。

読み込むファイル名を変更しただけでリコンパイルし、実行してみましたが、結果はまったく同じになります。

[Top Pageに戻る]

Ads by TOK2