RPG プログラムで郵便番号から住所を検索

郵便番号から住所を検索

サンプル・プログラム : GETZIP は郵便番号によって Webサービスを使って住所名を検索して
表示するための CGI です。

■ 郵便番号による検索の実行

サンプル初期画面

サンプル結果画面

サンプルの解説

■ 郵便番号検索API サイト(http://zip.cgis.biz/

この 郵便番号検索API サイト ( http://zip.cgis.biz/ ) では郵便番号をパラメータとして
このサイトに要求すると住所名が検索されて XML として戻されます。
つまりこのサイトは郵便番号検索の Webサービスを提供しているサイトです。
例えば、

http://zip.cgis.biz/xml/zip.php?zn=5700028

のように郵便番号 ( 570-0028 )をパラメータとして要求すると結果として次のような XML が戻ってきます。

  <?xml version="1.0" encoding="utf-8" ?> 
- <ZIP_result>
  <result name="ZipSearchXML" /> 
  <result version="1.01" /> 
  <result request_url="http%3A%2F%2Fzip.cgis.biz%2Fxml%2Fzip.php%3Fzn%3D5700028" /> 
  <result request_zip_num="5700028" /> 
  <result request_zip_version="none" /> 
  <result result_code="1" /> 
  <result result_zip_num="5700028" /> 
  <result result_zip_version="0" /> 
  <result result_values_count="1" /> 
- <ADDRESS_value>
  <value state_kana="オオサカフ" /> 
  <value city_kana="モリグチシ" /> 
  <value address_kana="ホンマチ" /> 
  <value company_kana="none" /> 
  <value state="大阪府" /> 
  <value city="守口市" /> 
  <value address="本町" /> 
  <value company="none" /> 
  </ADDRESS_value>
  </ZIP_result>

従って、このXML から住所名を取り出してやればよいことになります。
しかし問題は

  1. RPG プログラム (CGI) からどのように http://zip.cgis.biz/xml/zip.php?zn=5700028 をリクエストして、
    どのようにして XML を受け取ればよいのか ?
  2. 受け取った XML タグの中からどのようにして住所を取り出せばよいのか ?

という技術です。

■ XML パーサー ( XML_PARSE )

これらを簡単に可能にしてくれるのが EnterpriseServerXML パーサー ( XML_PARSE ) です。
XML パーサーとは XML を解析してプログラムが扱うことができる DOM ( 文書化モデル= Document Object Model )に
分解してくれるソフトウェアのことです。
EnterpriseServer の XML パーサーは XML_PARSE という名前のコマンドとして提供されています。
XML_PARSE は次のことを行います。

  • IFS に存在する XML でも、または Webサイトの別のサーバーにある XML でも
    接続して XML を読み取ることができる。
  • DOM は印刷またはファイル、ユーザー索引のいずれかとして出力することができる。

この XML パーサーはまた次の点において優れています。

  • URL を指定するだけで自動的に接続して取得することができる
  • Webサービス用の XML であっても URL を指定するだけで自動的に接続して取得することができます。
    ( Webサイトに接続できる XMLパーサーは他にはありません。)
  • 物理ファイルとして DOM を出力することができる
  • RPG, COBOL ユーザーが扱いなれている物理ファイルとして DOM を出力することができますので
    DOM を取り扱うための複雑なメソッドを学習する必要が全くありません。
    他のXMLパーサーでは DOM を扱うことが非常に困難です。
  • XML パーサー自身がコンパクトで軽量である
  • 他の XMLパーサーでは数10 MB の大容量であり System i 上では、とても実用的に
    扱える大きさでありません。しかし EnterpriseServer の XMLパーサーであれば軽量でコンパクト。
    適用業務の邪魔にはなりませんし高速で軽快に動作します。

■ XML パーサーの動作

EnterpriseServerXML パーサー なら動作は極めてシンプルです。

■ DOM (文書化モデル=Document Object Model ) の実際

DOM とは一体、どのような構造になっているのか紹介します。

  <?xml version="1.0" encoding="utf-8" ?> 
- <ZIP_result>
  <result name="ZipSearchXML" /> 
  <result version="1.01" /> 
  <result request_url="http%3A%2F%2Fzip.cgis.biz%2Fxml%2Fzip.php%3Fzn%3D5700028" /> 
  <result request_zip_num="5700028" /> 
  <result request_zip_version="none" /> 
  <result result_code="1" /> 
  <result result_zip_num="5700028" /> 
  <result result_zip_version="0" /> 
  <result result_values_count="1" /> 
- <ADDRESS_value>
  <value state_kana="オオサカフ" /> 
  <value city_kana="モリグチシ" /> 
  <value address_kana="ホンマチ" /> 
  <value company_kana="none" /> 
  <value state="大阪府" /> 
  <value city="守口市" /> 
  <value address="本町" /> 
  <value company="none" /> 
  </ADDRESS_value>
  </ZIP_result>

これを展開した DOM は次にようになります。

XMLタグ
001
XMLタグ
002
XMLタグ
003
・・・ XMLタグ
010
KEY
レベル
属性数 属性
001
属性
002
・・・ 属性
010
テキスト
ZIP_result
ZIP_result result 1 2 name="ZipSearchXML"
ZIP_result result 1 2 version="1.01"
ZIP_result ADDRESS_value value 1 2 state=" 大阪府 "
ZIP_result ADDRESS_value value 1 2 city=" 守口市 "

DOM の DDS ソース は次のとおりです。( ASNET.USR/QDDSSC(DOM) )

0001.00      A******************************************************            
0002.00      A*   DOM : DOCUMENT OBJECT MODEL FOR XML PARSER VER1.0             
0003.00      A******************************************************            
0004.00      A* 20 階層下までの XML に分解する                                  
0005.00      A*    タグの名前は親タグをすべて上位に付加して                     
0006.00      A*    N 番目の KEYXXN  として登録されている。                      
0007.00      A*    N 番目を示すのが KEYLVL である。                             
0008.00      A*    タグに囲まれた値はタグの値として KEYVAL に                   
0009.00      A*    登録されている。                                             
0010.00      A*                                                                 
0011.00      A*--------------------------------------------------------------   
0012.00      A*    XML は一般的に                                               
0013.00      A*      <MYTAG PRM001=AA PRM002=BBB>MYVALUE</MYTAG>                
0014.00      A*    の形式で保管されている。                                     
0015.00      A*                                                                 
0016.00      A*       MYTAG: タグ名  KEY001 - KEY020                            
0017.00      A*       PRM001=AA, PRM002=BBB: 属性  PRM001 - PRM020              
0018.00      A*       MYVALUE: 値 DOMTXT                                        
0019.00      A*--------------------------------------------------------------   
0020.00      A**                                    UNIQUE                      
0021.00      A          R DOMREC                    TEXT('DOM レコード ')       
0022.00      A*                                                                 
0023.00      A            KEY001        40O         COLHDG('XML タグ 001')      
0024.00      A            KEY002        40O         COLHDG('XML タグ 002')              
0025.00      A            KEY003        40O         COLHDG('XML タグ 003')              
0026.00      A            KEY004        40O         COLHDG('XML タグ 004')              
0027.00      A            KEY005        40O         COLHDG('XML タグ 005')              
0028.00      A            KEY006        40O         COLHDG('XML タグ 006')              
0029.00      A            KEY007        40O         COLHDG('XML タグ 007')              
0030.00      A            KEY008        40O         COLHDG('XML タグ 008')              
0031.00      A            KEY009        40O         COLHDG('XML タグ 009')              
0032.00      A            KEY010        40O         COLHDG('XML タグ 010')              
0033.00      A*                                                                         
0034.00      A            KEYLVL         4S 0       COLHDG('KEY レベル ')               
0035.00      A            PRMSU          2S 0       COLHDG(' 属性の数 ')                
0036.00      A*                                                                         
0037.00      A            PRM001       128O         COLHDG(' 属性 001')                 
0038.00      A            PRM002       128O         COLHDG(' 属性 002')                 
0039.00      A            PRM003       128O         COLHDG(' 属性 003')                 
0040.00      A            PRM004       128O         COLHDG(' 属性 004')                 
0041.00      A            PRM005       128O         COLHDG(' 属性 005')                 
0042.00      A            PRM006       128O         COLHDG(' 属性 006')                 
0043.00      A            PRM007       128O         COLHDG(' 属性 007')                 
0044.00      A            PRM008       128O         COLHDG(' 属性 008')                 
0045.00      A            PRM009       128O         COLHDG(' 属性 009')                 
0046.00      A            PRM010       128O         COLHDG(' 属性 010')                 
0047.00      A*                                                                         
0048.00      A            DOMTXT      2048O         COLHDG(' テキスト ')  
0049.00      A                                      VARLEN(255)           
0050.00      A*                                                           
0051.00      A          K KEY001                                          
0052.00      A          K KEY002                                          
0053.00      A          K KEY003                                          
0054.00      A          K KEY004                                          
0055.00      A          K KEY005                                          
0056.00      A          K KEY006                                          
0057.00      A          K KEY007                                          
0058.00      A          K KEY008                                          
0059.00      A          K KEY009                                          
0060.00      A          K KEY010                                          

これで DOM は XML をシンプルなデータ・ベースに展開したものであることがおわかりでしょう ?
そもそも XML の本質とは 構造化されたツリー構造のデータ・ベース表現 であり、これは
System i が最も得意とする物理ファイルに過ぎません。
しかし多くの XML パーサーが XML を物理ファイルとは別のモノであると扱っているために
折角、データ・ベースに得意なはずの IBM ユーザーの理解を得られなくなってしまっています。
これは System i の OS400 プラットフォームでの XML関連の開発者が XML の本質を正しく
理解できていないからに過ぎません。
XML を上記の物理ファイルとしての DOM に展開してしまえば IBM ユーザーには、このDOM をどのようにして
扱うべきか説明は不要でしょう。
あるIBM ユーザーはこうして解説している私より優れた処理方法を編み出すかもしれないからです。

RPG プログラム: GETZIP

それでは RPG で書かれた CGI : GETZIP のソースを紹介しましよう。

サンプル ソース・コード

001.00 H NOMAIN BNDDIR('ASNET.COM/MAIN') DATEDIT(*YMD/)                       
002.00 F**********  郵便番号の検索         ***********************************
003.00 F*  GETZIP      : TYPE: 単票表示    言語 : RPG#   VER 5.1              
004.00 F**********************************************************************
005.00 FGETZIPH   CF   E             SPECIAL PGMNAME('ASNET.COM/HTMLDVR')     
006.00 F                                     PLIST(HPARM)                     
007.00 F                                     INFDS(INFDSF)                    
008.00 FDOM       IF   E           K DISK    EXTFILE(DOM_LIB)                 
009.00 F                                     USROPN                           
010.00 F                                     INFDS(INFDSX)                    
011.00 F**********************************************************************
012.00                                                                        
013.00 F*---------------------------------------------------------------------
014.00 F* CRTMOD=CRTWEBMOD QTEMP/GETZIP SRCFILE(ASNET.USR/QRPGLESRC) +        
015.00 F*           OPTION(*NOXREF *NOSECLVL *NOEXPDDS *NOSHOWCPY) +          
016.00 F*           DBGVIEW(*SOURCE) AUT(*ALL)                                
017.00 F* CRTPGM=CRTPGM    CGIBIN/GETZIP MODULE(ASNET.COM/MAIN QTEMP/GETZIP) +
018.00 F*                  ACTGRP(*NEW) AUT(*ALL)                             
019.00 F* USER=QTMHHTTP                                                       
020.00 F* TYPE=RPG#                                                           
021.00 F* HTML=/AS400-NET.USR/PROJECT/GETZIP/DSPHEAD.HTM                      
022.00 F* HTML=/AS400-NET.USR/PROJECT/GETZIP/DSPDTA01.HTM                     
023.00 F* LIBL=CGIBIN    QTRFIL    QGPL      QTEMP     ASNET.COM              
024.00 F* CALL=HTTP://218.44.135.18/CGI-BIN/GETZIP.PGM                        
025.00 F* TEXT= 商品マスターファイル                                          
026.00 F* SESSION=*NO                                                         
027.00 F* KEEP-ALIVE=*NO                                                      
028.00 F* CCSID=5026                                                          
029.00 F* 作成日 :CRTDATE=2011/05/06  時刻 18:11:00   BY QTMHHTTP             
030.00 F* 変更日 :CHGDATE=2011/05/06  時刻 18:11:00   BY QTMHHTTP             
031.00 F*---------------------------------------------------------------------
032.00                                                                        
033.00  /COPY ASNET.USR/QRPGLESRC,PROTOTYP5#                                  
034.00 D DSPHEAD_GO      PR                                                   
035.00 D DSPDTA01_GO     PR                                                   
036.00 D XML_PARSE       PR                                                   
037.00 D  ZIP                          10A   Vアツマオ                            
038.00 D ATTR            PR            40A                                    
039.00 D  KEY                          15A   Vアツマオ                            
040.00 D HTM_FILE        S             10A   INZ('GETZIPH   ')                
041.00 D HTM_LIB         S             10A   INZ('*LIBL     ')                
042.00 D DOM_LIB         S             21    INZ('QTEMP/DOM')                 
043.00 D HTM_DIR         S            128A   INZ('/AS400-NET.USR/PROJECT/-    
044.00 D                                     GETZIP')                         
045.00 D OPNERR          C                   CONST('XML を取得できません。 ') 
046.00 D NOREC           C                   CONST('XML が空です。 ')         
047.00 D CMD1            S            128A   INZ('ASNET.COM/XML_PARSE PATH(-  
0048.00 D                                     ''http://zip.cgis.biz/xml-     
0049.00 D                                     /zip.php?zn=')                 
0050.00 D CMD2            S            128A   INZ(''') OUTPUT(*OUTFILE) -    
0051.00 D                                     OUTFILE(QTEMP/DOM)')           
0052.00 D CMD             S            180A                                  
0053.00                                                                      
0054.00 D*( ファイル情報データ構造 )                                         
0055.00 D INFDSF          DS                                                 
0056.00 D                              512A                                  
0057.00 D  HTM_RECORD       *RECORD                                          
0058.00 D INFDSX          DS                                                 
0059.00 D                              512A                                  
0060.00 D  FILREC               156    159B 0                                
0061.00 D*( プログラム状況データ構造 )                                       
0062.00 D INFDSP         SDS                                                 
0063.00 D                              512A                                  
0064.00  /COPY ASNET.USR/QRPGLESRC,INFDSP                                    
0065.00  /COPY ASNET.USR/QRPGLESRC,HPARM                                     
0066.00                                                                      
0067.00 *********************************************************            
0068.00 P EVENT           B                   EXPORT                         
0069.00 *********************************************************            
0070.00 D                 PI                                                 
0071.00  /FREE                                                               
0072.00     ON_CLICK('DSPHEAD': 'GO': %PADDR(DSPHEAD_GO));                     
0073.00     ON_CLICK('DSPDTA01': 'GO': %PADDR(DSPDTA01_GO));                   
0074.00  /END-FREE                                                             
0075.00 P                 E                                                    
0076.00 *********************************************************              
0077.00 P BEGIN           B                   EXPORT                           
0078.00 *********************************************************              
0079.00  * 最初の画面を出力します。                                            
0080.00 D                 PI                                                   
0081.00 C                   WRITE     DSPHEAD                                  
0082.00 P                 E                                                    
0083.00                                                                        
0084.00 *********************************************************              
0085.00 P DSPHEAD_GO      B                   EXPORT                           
0086.00 *********************************************************              
0087.00  * GO ボタンが押されたときの記述を行います。                           
0088.00 D                 PI                                                   
0089.00 C                   READ      DSPHEAD                              99  
0090.00 C                   CALLP     XML_PARSE(ZIPCODE)                       
0091.00 C                   OPEN      DOM                                  99  
0092.00 C     *IN99         IFEQ      *OFF                                     
0093.00 C                   READ      DOM                                    50
0094.00 C*                                                                     
0095.00 C     FILREC        IFEQ      *ZEROS                                   
0096.00 C                   CALLP     ALERTMSG(NOREC:'(KFLD)':'(KFLD)')        
0097.00 C                   WRITE     DSPHEAD                                  
0098.00 C                   RETURN                                             
0099.00 C                   END                                                
0100.00 C*                                                                     
0101.00 C                   EVAL      STATE = ATTR('state')                    
0102.00 C                   EVAL      STATE_KANA = ATTR('state_kana')          
0103.00 C                   EVAL      CITY  = ATTR('city')                     
0104.00 C                   EVAL      CITY_KANA  = ATTR('city_kana')           
0105.00 C                   EVAL      OTHER = ATTR('address')                  
0106.00 C                   EVAL      OTHER_KANA  = ATTR('address_kana')       
0107.00 C                   WRITE     DSPDTA01                                 
0108.00 C                   CLOSE     DOM                                      
0109.00 C                   ELSE                                               
0110.00 C                   CALLP     ALERTMSG(OPNERR:'(KFLD)':'(KFLD)')       
0111.00 C                   WRITE     DSPHEAD                                  
0112.00 C                   END                                                
0113.00 P                 E                                                    
0114.00                                                                        
0115.00 *********************************************************              
0116.00 P XML_PARSE       B                   EXPORT                           
0117.00 *********************************************************              
0118.00  * XML を WEB サービスで受信して QTEMP/DOM へ展開します *              
0119.00 D                 PI                                                   
0120.00 D  ZIP                          10A   Value                            
0121.00 C                   EVAL      CMD = %TRIMR(CMD1) + ZIP + %TRIM(CMD2)   
0122.00 C     ' '           CHECKR    CMD           L                 4 0      
0123.00 C                   Z-ADD     L             CMDLEN                     
0124.00 C*-------------------------------------------------------              
0125.00 C                   CALL      'QCMDEXC'                                
0126.00 C                   PARM                    CMD                        
0127.00 C                   PARM                    CMDLEN           15 5      
0128.00 C*-------------------------------------------------------              
0129.00 P                 E                                                    
0130.00                                                                        
0131.00 *********************************************************              
0132.00 P ATTR            B                   EXPORT                           
0133.00 *********************************************************              
0134.00  * DOM の指定された属性のものを抜き出します。                          
0135.00 D                 PI            40A                                    
0136.00 D  KEY                          15A   Value                            
0137.00                                                                        
0138.00 D RTNVAL          S             80A                                    
0139.00 D AR              S              1    DIM(128)                         
0140.00                                                                        
0141.00 C                   CAT       '=':0         KEY                        
0142.00 C     ' '           CHECKR    KEY           L                 4 0      
0143.00 C     *LOVAL        SETLL     DOM                                      
0144.00 C                   DO        *HIVAL                                   
0145.00 C                   READ      DOM                                    50
0146.00 C   50              LEAVE                                              
0147.00 C     KEY:L         SCAN      PRM001        P                 4 0    50
0148.00 C     *IN50         IFEQ      *ON                                      
0149.00 C                   ADD       L             P                          
0150.00 C                   ADD       1             P                          
0151.00 C     80            SUBST(P)  PRM001:P      RTNVAL                     
0152.00 C                   MOVEA(P)  RTNVAL        AR                         
0153.00 C                   Z-ADD     1             N                 4 0      
0154.00 C     '"'           LOOKUP    AR(N)                                  50
0155.00 C   50              MOVE      ' '           AR(N)                      
0156.00 C   50              MOVEA(P)  AR            RTNVAL                     
0157.00 C                   RETURN    RTNVAL                                   
0158.00 C                   END                                                
0159.00 C                   END                                                
0160.00 C                   MOVEL(P)  '*ERROR*'     RTNVAL                     
0161.00 C                   RETURN    RTNVAL                                   
0162.00 P                 E                                                    
0163.00                                                                        
0164.00 *********************************************************              
0165.00 P DSPDTA01_GO     B                   EXPORT                           
0166.00 *********************************************************              
0167.00  * GO ボタンが押されたときの記述を行います。                           
0168.00 D                 PI                                                  
0169.00 C                   READ      DSPDTA01                             99 
0170.00 C                   WRITE     DSPDTA01                                
0171.00 P                 E                                                   
0172.00                                                                       
0173.00 *********************************************************             
0174.00 P END             B                   EXPORT                          
0175.00 *********************************************************             
0176.00  * 最後の処理を記述します。                                           
0177.00 D                 PI                                                  
0178.00 C                   CLOSE     GETZIPH                                 
0179.00 C                   CLOSE     DOM                                     
0180.00 P                 E                                                   
					

■ 解説

このわずか 180ステップ数のプログラム自身は難しいものではないはずです。
特に今回の DOM は非常に簡単であり、RUNQRY *NONE QTEMP/DOM で DOM に
どのような値が書かれているかを見れば、

         属性 001
                                                     
  name="ZipSearchXML"                                
  version="1.01"                                     
  request_url="http%3A%2F%2Fzip.cgis.biz%2Fxml%2Fzip.
  request_zip_num="5700028"                          
  request_zip_version="none"                         
  result_code="1"                                    
  result_zip_num="5700028"                           
  result_zip_version="0"                             
  result_values_count="1"                            
                                                     
  state_kana="オオサカフ"                                 
  city_kana="モリグチシ"                                 
  address_kana="ホンマチ"                                
  company_kana="none"                                
  state=" 大阪府 "                                   
  city=" 守口市 "                                    
  address=" 本町 "                                   
  company="none"         

のように 属性値 001 ( PRM001 ) に求める答えがすべて書かれていることがわかりますので
すべての DOM レコードを読み取って 属性値001 だけを調べればよいことになります。
DOM や XML がどのような構造をしているか等を全く考慮する必要もありません。
QTEMP/DOM

                  *LOVAL     SETLL  DOM
                             DO     *HIVAL
                             READ   DOM                  50
                              :
                             END

のようにして、すべて読み取って

                  KEY:L         SCAN      PRM001        P 

のよにして、PRM001 に state= というような文字列があるかどうかを調べればよいだけです。
このように RPG プログラムによって、こんなに簡単に XML を調べることができるのも
XML パーサーが DOM をデータ・ベースに変換してくれるからです。
しかも DOM というデータ・ベースに変換された後も XML の本質は維持されているのです。

コンパイルの前には コマンド: XML_PARSE を次のように使って QTEMP/DOM を作成しておいてください。

XML_PARSE PATH('http://zip.cgis.biz/xml/zip.php?zn=5700085') OUTPUT(*OUTFILE) OUTFILE(QTEMP/DOM)

このコマンドを実行できたら

RUNQRY *NONE QTEMP/DOM

によってデータ・ベース QTEMP/DOM が正しく作成されたかどうかを確認してください。
RPG : GETZIPRPG# で書かれてしますので

CRTWEBMOD QTEMP/GETZIP SRCFILE(MYSRCLIB/QRPGLESRC) AUT(*ALL)

CRTPGM CGIBIN/GETZIP MODULE(ASNET.COM/MAIN QTEMP/GETZIP) ACTGRP(*NEW) AUT(*ALL)

によって行います。

サンプル結果画面

いろいろな郵便番号を入れて試してみてください。
「GO」ボタンを押した瞬間に GETZIP は System i からインターネット経由で 郵便番号検索API のサイトら
接続しに行って結果の XML を受信します。
あなたの IBM System i 自身が初めてインターネット経由で他のサーバーに接続して会話した瞬間のはずです。
素晴らしいとは思いませんか ?
今回は URL の様子からすると PHP と対話したようですが、この方法でどのようなプログラムでも
インターネット経由であなたのプログラムは会話することができます。

郵便番号による住所検索だけでなく

  • 交通費清算 Webサービス
  • 所得税計算 Webサービス

など等 Google, Yahoo!, Amazon などでもビジネスに使える Webサービスは急速に拡大してきています。
ご自身でも調べてみてください。