XML 文書の処理

XML-INTO または XML-SAX ステートメントを使用して RPG プログラムから XML 文書を処理することができます。このステートメントは、高速 XML パーサーへの RPG 言語インターフェースです。RPG で現在使用されているパーサーは、多数の適格性エラーに対して XML 文書を検査しますが、非有効化パーサーです。 XML パーサーについて詳しくは、「ILE COBOL プログラマーの手引き」の付録『XML 参照資料』にある『XML 適合性』節を参照してください。

XML 文書は、文字でも UCS-2 RPG 変数でもよく、または統合ファイル・システムのファイル内にあってもかまいません。

パーサーは、SAX パーサーです。SAX パーサーは、XML 文書を文字単位で読み込んで動作します。要素名、または属性値などの XML 文書の断片を見つけたときはいつでも、パーサーの発呼者が提供する処理手順に答えて、見つけた XML の断片に関する情報を渡します。例えば、パーサーが XML 要素名を見つけた場合、パーサーは、"イベント" が "start element" イベントであることを示す処理手順を呼び出し、エレメントの名前を渡します。

処理手順は、情報を処理し、別のイベントで処理手順を呼び出すのに十分な情報を持つまで XML 文書を読み込み続けるパーサーに戻ります。このプロセスは、XML 文書全体が構文解析されるか、処理手順が構文解析を終了する必要があることを示すまで、繰り返されます。

例えば、以下の XML 文書について考えてみてください。

<email type="text">
  <sendto>JohnDoe@there</sendto>
</email>

以下は、パーサーが読み込むテキストの断片、パーサーが生成するイベント、および各イベントに関連するデータです。注: 用語 "whitespace" は、行の終わりの文字、タブ文字およびブランクを指します。

構文解析されたテキスト イベント イベント・データ
start document
<email start element "email"
type= attribute name "type"
"text" attribute value "text"
>whitespace element content the whitespace
<sendto> start element "sendto"
JohnDoe@there element content "JohnDoe@there"
</sendto> end element "sendto"
whitespace element content the whitespace
</email> end element "email"
end document

XML-SAX および XML-INTO 命令コードでは、XML パーサーを使用することができます。

  1. XML-SAX 命令では、イベント処理手順を指定し、パーサーが生成するすべてのイベントを処理できます。 XML 文書に何が含まれているかを事前に知らない場合は、これが便利です。

    例えば、XML 文書が type という名前の XML 属性を含むことが分かっていて、この属性の値を知りたい場合は、処理手順は "attribute name" イベントが "type" の値を持つまで待つことができます。 次回呼び出されるハンドラーは、必要なデータ (上記の例では "text") を持つ "attribute value" イベントである必要があります。

  2. XML-INTO 命令では、XML 文書の内容を RPG 変数に直接読み込むことができます。XML 文書の形式が分かっており、文書の XML 要素の名前が RPG 変数に付けた名前と同じになることが分かっている場合は、これが便利です。

    例えば、XML 文書が常に上記の文書の形式を持っていることが分かっている場合は、 名前 "email"、およびサブフィールド "type" および "sendto" を持つ RPG データ構造を定義することができます。次に、XML-INTO 命令を使用して 、XML 文書をデータ構造に直接読み込むことができます。命令が完了したとき、 "type" サブフィールドは値 "text" を持ち、"sendto" サブフィールドは 値 "JohnDoe@there" を持ちます。

  3. XML-INTO 命令では、繰り返される XML エレメントの不明な数の値を取得することもできます。ユーザーは、処理手順が呼び出されるたびに要素の固定数の値を受け取る処理手順を提供します。XML 文書が一連の同一の XML 要素を含むことが分かっており、それがいくつになるか事前に分からない場合は、これが便利です。

XML データは、常にテキスト形式でパーサーによって戻されます。データが数値データ、または日付データなどの他のデータ・タイプを表すことが分かっている場合、XML-SAX 処理手順は、%INT または %DATE などの変換関数を使用してデータを変換する必要があります。

XML-INTO 命令は、文字データをレシーバーとして指定してフィールドまたはサブフィールドのタイプに自動的に変換します。

XML-SAX および XML-INTO 命令の両方で、命令を制御する一連のオプションを指定できます。 このオプションは、以下の形式の 1 つの文字式で指定されます。

'opt1=val1 opt2=val2'

各命令は、独自の有効なオプションのセットを持っています。両方の命令コードに共通なオプションは以下のとおりです。

doc
"doc" オプションは、ユーザーが命令に提供した XML 文書が、文書を含む統合ファイル・システムのファイルの名前なのか、文書自身であるのかを指定します。デフォルトは、ユーザーが実際の XML 文書を提供したことを示す "doc=string" です。オプション "doc=file" は、ユーザーが実際の XML 文書を含むファイルの名前を提供したことを示すために使用します。
ccsid
"ccsid" オプションは、XML パーサーがデータを戻す CCSID を指定します。XML-SAX 命令の場合、パーサーがサポートする任意の CCSID を指定できます。XML-INTO 命令の場合、構文解析が 1 バイト文字または UCS-2 のいずれで実行されるのかのみを制御できます。 このそれぞれの命令の "ccsid" オプションについて詳しくは、「ILE RPG 解説書」の情報を参照してください。

XML パーサー・エラー・コード

XML パーサーが構文解析時に XML 文書でエラーを検出した場合、 メッセージ RNX0351 が出力されます。メッセージから、エラーが発見された文書内のオフセットと同様に、エラーに関連する特定のエラー・コードを得ることができます。

以下の表は、各パーサー・エラー・コードの意味を示します。

XML パーサー・エラー・コード 説明
1 パーサーは、要素コンテントの外側の空白文字をスキャン中に無効な文字を見つけました。
2 パーサーは、要素コンテントの外側に、処理命令、要素、コメント、または文書タイプ宣言の無効な開始を見つけました。
3 パーサーは、重複属性名を見つけました。
4 パーサーは、属性値にマークアップ文字 '<' を見つけました。
5 要素の開始および終了タグ名が一致しませんでした。
6 パーサーは、要素コンテントに無効文字を見つけました。
7 パーサーは、要素コンテントに、要素、コメント、処理命令、または CDATA セクションの無効な開始を見つけました。
8 パーサーは、要素コンテントに、マッチする開始文字シーケンス '<![CDATA[' のない CDATA 終了文字シーケンス ']]>' を見つけました。
9 パーサーは、コメントに無効文字を見つけました。
10 パーサーは、コメントに、'>' が続かない文字シーケンス '--' (2 つのハイフン) を見つけました。
11 パーサーは、処理命令データ・セグメントに無効文字を見つけました。
12 処理命令ターゲット名は、小文字、大文字、または大/小文字混合の 'xml' でした。
13 パーサーは、16 進文字参照に無効桁を見つけました (形式 &#xdddd;、例えば &#x0eb1)。
14 パーサーは、小数点文字参照に無効桁を見つけました (形式 &#dddd;)。
15 文字参照は、合法な XML 文字を参照しませんでした。
16 パーサーは、エンティティー参照名に無効文字を見つけました。
17 パーサーは、属性値に無効文字を見つけました。
18 パーサーは、起こりうる文書タイプ宣言の無効開始を見つけました。
19 パーサーは、2 番目の文書タイプ宣言を見つけました。
20 要素名は正しく指定されませんでした。最初の文字が文字 '_' または ':' でなかったか、または、パーサーが要素名内または要素名に続いて無効文字を見つけました。
21 属性は正しく指定されませんでした。属性名の最初の文字が文字 '_' または ':' でなかったか、または '=' 以外の文字が属性名に続いて見つかったか、値の区切り文字のいずれかが不正確だったか、無効文字がその名前内またはその名前に続いて見つかりました。
22 空の要素タグが '/' が続く '>' で終了しませんでした。
23 要素終了タグが正しく指定されませんでした。最初の文字が文字 '_' または ':' でなかったか、またはタグが '>' で終了しませんでした。
24 パーサーは、要素コンテントにコメントまたは CDATA セクションの無効な開始を見つけました。
25 処理命令ターゲット名は、正しく指定されませんでした。処理命令ターゲット名の最初の文字が文字 '_' または ':' でなかったか、または、パーサーが処理命令ターゲット名内またはそれに続いて無効文字を見つけました。
26 処理命令が終了文字シーケンス '?>' で終了しませんでした。
27 パーサーは、文字参照またはエンティティー参照で、'&' に続いて無効文字を見つけました。
28 XML 宣言にバージョン情報が存在しませんでした。
29 XML 宣言で 'version' が正しく指定されませんでした。'version' の後に '=' が続かなかったか、値が欠落していたか、不適切に区切り文字で区切られていたか、値が不正な文字を指定したか、開始および終了区切り文字が一致しなかったか、パーサーが XML 宣言でバージョン情報値終了区切り文字に続いて無効文字を見つけました。
30 パーサーは、XML 宣言にオプションのエンコード宣言の代わりに無効属性を見つけました。
31 XML 宣言のエンコード宣言値が欠落しているか、不正でした。 値が小文字または大文字の A から Z で始まらなかったか、 'encoding' の後に '=' が続かなかったか、値が欠落していたか、不適切に区切り文字で区切られていたか、不正な文字を指定したか、開始および終了区切り文字が一致しなかったか、パーサーが終了区切り文字に続いて無効文字を見つけました。
32 パーサーは、XML 宣言にオプションのスタンドアロン宣言の代わりに無効属性を見つけました。
33 XML 宣言で 'standalone' 属性が正しく指定されませんでした。'standalone' の後に '=' が続かなかったか、値が欠落していたか、または不適切に区切り文字で区切られていたか、値が 'yes' でも 'no' でもなかったか、値が不正な文字を指定したか、開始および終了区切り文字が一致しなかったか、パーサーが終了区切り文字に続いて無効文字を見つけました。
34 XML 宣言は、適切な文字シーケンス '?>' で終了しなかったか、無効属性を含んでいました。
35 パーサーは、ルート要素の終了の後に文書タイプ宣言の開始を見つけました。
36 パーサーは、ルート要素の終了の後に要素の開始を見つけました。
300 パーサーは、文書が完了する前に文書の終わりに到達しました。
301 XML-INTO または XML-SAX の %HANDLER プロシージャーが非ゼロ値を戻し、XML 構文解析が終了しました。
302 パーサーが要求された CCSID 値をサポートしないか、XML 文書の最初の文字が '<' ではありませんでした。
303 文書は、パーサーが処理するには大き過ぎました。パーサーは不完全な文書を構文解析しようとしましたが、構文解析の完了には文書の終わりのデータが必要でした。
500-999 外部パーサーでの内部エラーです。サービス技術員にエラーを報告してください。
10001-19999 パーサーでの内部エラーです。サービス技術員にエラーを報告してください。

XML パーサーの制限

図 76. 統合ファイル・システムのファイルへのデータの書き込み
 * Parameters:
 * 1. path      : a pointer to a null-terminated string containing
 *                the path to the file to be written
 * 2. dataPtr   : a pointer to the data to be written
 * 3. dataLen   : the length of the data in bytes
 * 4. dataCcsid : the CCSID of the data
 * 5. fileCcsid : the desired CCSID of the file
 * Sample RPG coding:
 *   ifsWrite ('/home/mydir/temp.xml' : xmlPtr : xmlLen : 37 : 37);
 *   xml-into ds %xml('/home/mydir/temp.xml' : 'doc=file');
 * To delete the file, use the system command
 *   rmvlnk '/home/mydir/temp.xml'
 * Note: This module requires BNDDIR(QC2LE)
 P ifsWrite        B                   EXPORT
D ifsWrite        PI
D  path                           *   VALUE OPTIONS(*STRING)
D  dataPtr                        *   VALUE
D  dataLen                      10I 0 VALUE
D  dataCcsid                    10I 0 VALUE
D  fileCcsid                    10I 0 VALUE

D O_CREAT         C                   x'00000008'
D O_TRUNC         C                   x'00000040'
D O_WRONLY        C                   x'00000002'
D O_RDWR          C                   x'00000004'
D O_CCSID         C                   x'00000020'
D O_TEXT_CREAT    C                   x'02000000'
D O_TEXTDATA      C                   x'01000000'
D O_SHARE_NONE    C                   x'00080000'

D S_IRUSR         C                   x'0100'
D S_IROTH         C                   x'0004'
D S_IRGRP         C                   x'0020'
D S_IWUSR         C                   x'0080'
D S_IWOTH         C                   x'0002'

   
 
D ssize_t         S             10I 0
D size_t          S             10U 0

D open            PR            10I 0 EXTPROC('open')
D   path                          *   VALUE OPTIONS(*STRING)
D   flag                        10I 0 VALUE
D   mode                        10I 0 VALUE
D   fileCcsid                   10I 0 VALUE options(*nopass)
D   dataCcsid                   10I 0 VALUE options(*nopass)
D writeFile       PR                  LIKE(ssize_t)
D                                     EXTPROC('write')
D   handle                      10I 0 VALUE
D   data                          *   VALUE
D   len                               VALUE LIKE(size_t)
D closeFile       PR            10I 0 EXTPROC('close')
D   handle                      10I 0 VALUE

D oflag           S             10I 0
D omode           S             10I 0
D handle          S             10I 0
D rc              S             10I 0

D sysErrno        PR              *   EXTPROC('__errno')
D errno           S             10I 0 BASED(pErrno)
 /FREE
   pErrno = sysErrno();
   oflag = 0 + O_WRONLY + O_CREAT + O_TEXT_CREAT + O_TRUNC
         + O_CCSID + O_TEXTDATA + O_SHARE_NONE;
   omode = 0 + S_IRUSR + S_IWUSR + S_IRGRP + S_IROTH;

   handle = open(path : oflag : omode : fileCcsid : dataCcsid);
// insert error handling if handle is less than zero
      
   rc = writeFile (handle : dataPtr : dataLen);
   // insert error handling if rc is not zero
      
   rc = closeFile (handle);
   // insert error handling if rc is not zero
      
 /END-FREE
P ifswrite        E