RPGによるXML処理について、第1部:基本編
注:この記事に記載のコードはここからダウンロードできます。
RPG IV の組み込み XML サポートが現在利用できるようになっていますが、元々は 2006 年に V5R4 で紹介されていました。そのリリースには欠陥があったため、多くの RPG ユーザーは、 XML の処理に RPG を使用しないことにしました。幸運にも IBM がこれらの欠陥を修正し、RPG はスマートに XML を読み取るようになりました。その様子をお話しします。
その最初のリリースでは、最も強力な命令コード XML-INTO には、そのサポートにおいてギャップがあり、XML を前処理しないと使いづらい場合がありました。また、V6 以前の変数、データ構造、配列のサイズ制限により、非常に小さな XML 文書以外は、処理が難しいことがしばしばありました。その結果、そのコードを採用した RPG ユーザーの数は比較的少なかったのです。XML-INTO の弟分である XML-SAX に切り替えたユーザーもいました。Java、PHP、またはサード・パーティーのツールに戻ったユーザーもいました。
これはすべて、V6 が登場し、多くのユーザーにとって障害だった、さまざまなサイズ制限を緩和してから変わり始めました。続いて、残りの問題のほとんどは V6 および V7 の両リリースで、 PTF により修復されました。その結果、XML 文書が Web サービス・コールの応答として、ファイルまたは変数の形式の、いずれで受信されても処理できる、強力で使いやすい方法が生まれました。
このヒントのシリーズでは、基本から始め、進むにしたがって、より難しい事態が発生した場合の扱い方を図で示していきます。それはさておき、では始めましょう。
XML-INTO:「山を動かす」命令コード
RPG の XML サポートの核は XML-INTO です。演算項目を 2 つしか使用しておらず、一見すると表面上は単純な命令コードに見えます。最初の演算項目は、抽出データのターゲットを特定し、2 番目は XML ソースの情報を特定します。おわかりのように、影に隠れて多くのことを行い、まさにそうであるため、EXFMT といった他の強力な命令とともに「山を動かす」カテゴリーへと入れました。
以下は基本的な構文です。
XML-INTO xmlTarget %XML( xmlSource : processingOptions );
「xmlTarget」は抽出データを配置する場所です。単純なフィールドから配列、データ構造 (DS)、DS 配列まで、ほぼ何でもかまいません。リストは続きます。そのように多くの可能性があるのは、ターゲットの「シェイプ」(構造など) が元の XML 文書と一致しなければならないためです。RPG は変数の名前およびその階層から、そのシェイプを判断します。ここにそうしたケースが 1 つありますが、いくつかのシンプルな例で、私の言っていることをはるかに簡単に説明します。
XML 文書に以下のように、多数の顧客のアドレス情報が含まれているとします。
<Customers> <Customer> <Name>Brown and Sons</Name> <City>San Jose</City> <State>CA</State> </Customer> <Customer> <Name>Smith and Jones Inc. </Name> .... </Customers>
ターゲットは以下のようになります。
d customer ds Dim(99) Qualified d name 40a d city 40a d state 2a
<Customer> エレメントは繰り返しているため、配列として表現する必要があります。また、複合エレメント (Name、City、State の 3 エレメントで構成) であるため、RPG 用語では DS と表現する必要があります。この例では、Name、City、State の各フィールドを XML 文書の場合と同じ順序で配置している点に注意してください。これは重要ではありませんが、この場合では理解することができます。XML-INTO 用語では、フィールドが顧客の従属といった同じ階層位置にあることが唯一の要件です。この点は 2 つ目の例でお分かりになると思います。私が故意に順序を変えています。
XML 文書が、ディレクトリー XMLDocs の IFS ファイル Customers.xml に含まれていると仮定した場合、文書の処理に必要な XML-INTO 命令は以下のようになります。
XML-INTO customer %XML( fileLocn:'doc=file case=any');
ここで文字変数 fileLocn には値 /XMLDocs/Customers.xml が含まれています。
ここで 2 つの処理オプションを使用していることに注意してください。最初の「doc=file」は変数「fileLocn」に処理するファイルの名前が含まれていることをコンパイラーに指示しています。このオプションがないと、RPG コンパイラーは変数が XML を含めたと考えます。このオプションを追加し、忘れる (我々皆そうですが) と、実行時に XML 文書が有効ではないようだというランタイム・エラーが発生します。XML のようにファイル名を処理しようとしているので、無理はありません。
2 つ目のオプション「case=any」は、XML-INTO を使用する場合には必ず使用しなければなりません。なぜでしょうか? XML エレメントは大文字と小文字を区別し、RPG 定義と一致させるには、まず名前を大文字に変換する必要があります。すべての RPG 名は、コンパイラーにそのように認識されるためです。
いったん XML-INTO 命令が完了すると、あとは取得したデータを処理するだけです。そのためには、いくつの顧客エレメントが検出されたか、ほぼ確実に判断する必要があります。RPG は、プログラム・ステータス・データ構造 (PSDS) の位置 372 から 379 に埋められたエレメントの数を 20 桁の整数で数えることで、非常にシンプルなソリューションを提供します。このカウントを使用して、埋められた各エレメントをループして、処理することができます。私のサンプル・プログラムで、その様子がわかると思います。例にあるように、XML-INTO のターゲットが配列の場合にのみ、カウント・フィールドは有効です。XML サポートのオリジナル・リリースでは、この種のエレメント・カウントのみサポートされていました。このシリーズの次の部では、機能強化されたサポートについて学びます。
より複雑な例
ここまで、XML 文書でこれほどシンプルなものはほとんどないことが確実にわかったと思います。例えば、Customer ID が属性として文書に組み込まれており、City および State が Address という名前の複合エレメントの一部だとしたらどうでしょうか。そうした文書の XML は以下のようになります。
<Customers> <Customer Id="B012345"> <Name>Brown and Sons</Name> <Address> <City>San Jose</City> <State>CA</State> </Address> </Customer>
複合エレメントは RPG 用語でいう DS にマッピングされるため、ある DS (Address) を別の DS (Customer) に入れ子にする必要があります。幸運にも RPG は V5R2 でこの機能を提供していました。しかし、属性 ID の処理方法はどうでしょうか? これも非常に簡単であることがわかります。基本的に、エレメントの属性は、そのエレメントの子として同じ階層レベルにあるものとして処理されます。そのため、RPG 構造のコーディング方法は XML と全く同じです。
<Customers> <Customer> <Id>B012345</Id>
この改定されたフォーマットの処理に必要な変更を以下に示します。
d customer ds Dim(99) Qualified d id 7a d name 40a d address LikeDS(address_T) // V5R4 システムの場合はキーワード「Template」を削除 d address_T ds Template d state 2a d city 40a
おわかりのように LikeDS キーワードを使用することで、「address」DS を元の「customer」DS 内に入れ子にすることができました。また、構造に「id」フィールドを追加しました。
こうした入れ子データ構造に不慣れな方のために、特定の顧客エレメントの ID フィールドを参照するには、customer(index).id を参照してくださいとだけ指摘しておきます。アドレスの state フィールドを参照するには、customer(index).address.state をコーディングします。入れ子構造の DS および DS 配列の詳細は、この記事をご覧ください。
私の例では、「address_T」構造を記述するのにキーワード「Template」を使用している点に注意してください。これがないと、address_T DS はメモリーを無駄にするだけになります。Template は V6 で導入されました。V5R4 に対してこの例をコンパイルする必要がある場合、単にそのキーワードを削除してください。
これらの例を試してみたい方は、コードをここからダウンロードできます。