RPG で XML 文書を処理する (その 1)

XML というものがポピュラーになるにつれ、データベースとやり取りをしたい、というニーズはけっこう多くなってきているような気がします。

XML エクステンダーというものがありましたが、本家の UDB ではすでに「エクステンダー」ものはすべてなくなってしまい、今は pureXML が XML 処理については推奨になっています。( ...... というか最新の UDB のサイトにはXML エクステンダーの話は影も形もありません ...... IBM さん、どういうつもりなんでしょうか)

i5/OS + ILE/RPG の機能として V5R4 から XML 処理がより簡単にできるようになりました。
System i のプログラミング人口のかなり多くを占めるであろう RPG のプログラマーにとって、XML 文書の処理とは、極端なことを言えば、書き出しはもともとそんなに出来ないことではなかったのですが、やはり XML 文書を読み込んで処理をする、というところに難しさがあるように思います。
今回の RPG の機能拡張は、まずは簡単に XML 文書が一命令で処理できる、というのが手始めのよいところではないでしょうか。

IBM のサイトなどでもあまり紹介されてないかんじなので、ちょっとまとめてみました。


XML-INTO 命令

XML 文書を一息に読み込んでしまう XML-INTO という命令がサポートされるようになりました。
どんなふうに動くのか確認するために、「A Traditional Approach to a Modern Technology」という記事を参考にしてサンプルのプログラムを作ってみました。

読み込むサンプルになる XML 文書はこんなかんじのものです。記事の中からそのままとってきました。(内容に日本語のところを足しています)

<Customers>                                 
  <RecordCount> 25 </RecordCount>           
  <Customer type="wholesale">               
    <Contact>John Jones</Contact>           
    <Company>Phones R us</Company>          
    <Address>                               
      <Street>5678 High Street</Street>     
      <City>MyTown</City>                   
      <State>GA</State>                     
      <zip>30033</zip>                      
    </Address>                              
  </Customer>                               
  <Customer type="retail">                  
    <Contact>Meanold Miser</Contact>        
    <Company>Suchadeal Bank</Company>       
    <Address>                               
      <Street>91011 Important Ave.</Street> 
      <City>Bankville</City>                
      <State>MN</State>                       
      <zip>55901</zip>                        
    </Address>                                
  </Customer>                                 
  <Customer type=" その他 ">                  
    <Contact> 西 一 </Contact>               
    <Company> 東西商事 </Company>             
    <Address>                                 
      <Street> 本町 </Street>                 
      <City> 大阪 </City>                     
      <State>NA</State>                       
      <zip>12345</zip>                        
    </Address>                                
  </Customer>                                 
</Customers>                                  

さすがに RPG でアクセスするわけですから、やはり i5/OS 上にある必要があります。
FTP か何かで IFS に転送しておけば OK です。

5250 画面から見るとこんなかんじになります。

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

D 仕様書で、読み込んだ XML 文書のそれぞれの項目を格納するフィールドを定義します。

XML 文書というもの自体が構造を持っているわけですから、格納先もやはりデータ・ストラクチャーを使うのが一般的でしょう。
ここを巧く構造をシミュレートして、データ・ストラクチャーを作るのが思案のしどころの大半です。

後で取り出すことを考えればやはり Qualified で修飾できないと不便ですし、LikeDS を使用した入れ子構造や、繰り返しに対応するためのデータ・ストラクチャーの配列など、やはり V5R4 まで進化してきた ILE/RPG で初めてこの XML-INTO 命令が出てきた、というは理解できるような気がします。

XML-INTO 命令は前掲の記事に載っている形態をすべてそのまま使ってみました。

allowextra=yes というのは、たとえば XML 文書に <tel> といった要素があるのにデータ・ストラクチャーには tel というサブフィールドがない場合にも処理を継続する、というオプションです。
デフォルトでは 353 (element mismatch) という状況コード (%STATUS でひろいます) と共に処理は終了してしまいます。

allowmissing=yes というのは、allowextra とは逆にデータ・ストラクチャーに存在するサブフィールドについて XML 文書の方に対応する名前の要素がない、という場合にも処理を継続する、というオプションになります。
状況コードはやはり 353 で処理の終了についても同じことになります。

D recordCount     S              5p 0                                              
 *                                                                                 
D Customers       DS                  Qualified                                    
D  customer                           LikeDS(customer)                             
D                                     dim(99)                                      
 *                                                                                 
D Customer        DS                  Qualified                                    
D  type                         10a                                                
D  company                      32a                                                
D  address                            LikeDS(address)                              
 *                                                                                 
D Address         DS                  Qualified                                    
D  Street                       32a                                                
D  city                         24a                                                
D  state                         2a                                                
D  zip                           5s 0                                              
 *                                                                                 
 /Free                                                                             
  //  全件が配列に入ってくる                                                       
  XML-INTO Customers.customer  %XML('/home/XML/customer.xml' :  'doc=file +            
                         case=any allowextra=yes allowmissing=yes') ;                  
                                                                                       
  //  全件が配列に入ってくる                                                           
  XML-INTO Customers  %XML('/home/XML/customer.xml' :  'doc=file +                     
                         case=any allowextra=yes allowmissing=yes') ;                  
                                                                                       
  //   <RecordCount> の内容のみが入ってくる                                            
  XML-INTO recordCount  %XML('/home/XML/customer.xml' :  'doc=file +                   
                         path=Customers/RecordCount +                                  
                         case=any allowextra=yes allowmissing=yes') ;                  
                                                                                       
  //  最初の一件のみが入ってくる                                                       
  XML-INTO customer     %XML('/home/XML/customer.xml' :  'doc=file +                   
                         path=Customers/customer +                                     
                         case=any allowextra=yes allowmissing=yes') ;                  
                                                                                       
  *inLR=*on ;                                                                          
  return ;                                                                             
 /End-Free

それぞれの XML-INTO 命令の実行結果です。

最初のものはデータ・ストラクチャーの一部分に読み込むやり方になっています。
もともとのデータ・ストラクチャーが配列になっていて、その一部分に対して名前で対応する部分を取ってきます。つまり、Customers というデータ・ストラクチャーの配列の Customer という部分は <Customers> の中の <Customer> の繰り返しに対応しているわけです。

二番目のものは、一番目のものとまったく同じ処理になります。
<Customer> の繰り返しを含む <Customers> をすべて Customers というデータ・ストラクチャーの配列に格納する、という解釈になりますね。
データ・ストラクチャー内に対応するもののない <RecourdCount> については格納先がないので取得されません。

三番目のものは <RecourdCount> だけを取ってくるやり方です。

また、配列でないデータ・ストラクチャーに対してもともと繰り返しのある要素を格納しようとすると最初の一件だけが取得されてきます。それが四番目のやり方になります。

この四番目の結果を見て、次の結果を取得するためにひょっとしたらこんなコードを書きたくなるかもしれませんが、XML-INTO 命令は毎回新たに XML 文書を読んでくるだけなので毎回同じデータが取得されてくるだけになってしまいます。

  // BAD                                                                  
  For i=1 to recordCount ;                                                
  //    毎回最初の一件のみが入ってくる                                    
  XML-INTO customer     %XML('/home/XML/customer.xml' :  'doc=file +      
                         path=Customers/customer +                        
                         case=any allowextra=yes allowmissing=yes') ;     
      If customer = *blanks ;                                             
      leave ;                                                             
      endIf ;                                                             
   EndFor ;

この XML-INTO 命令の原則は、一回で取って来ること、なわけですね。そのためにデータ構造などを工夫する必要があるわけです。

繰り返しの回数、つまり配列の行数を動的に変更することが RPG では (簡単には) できないので、あらかじめ配列をどれだけ取っておくか、は思案のしどころです。
この例では 99 個とってあるので、空のデータ部分がけっこうあります。これをプログラムの中で、空だったらそれ以降は処理しない、といったようなロジックを組み込む必要があります。
また、もっとたくさんあるような場合は %Handler 関数などを使ってさらにきめ細かい取得の仕方を書くことで対処できます。(%Handler 関数の使い方はこちらを参照してみてください)

別の XML ファイルを使って同様にサンプルを作ってみたのがこちらの記事になりますので、よかったら参考にしてください。

[Top Pageに戻る]

Ads by TOK2