データベース

39. XML-INTO でJSONを解析(1)

先に他のシステムとのデータ交換の形式にXMLが普及していると
紹介したが現在ではさらに進んでJSONというXMLの拡張形式が
一般的である。
実はインターネットの世界でJavaScriptや他のシステムとのデータ交換の
型式はXMLというよりむしろJSONがもはや一般的なのである。
IBMも i5/OS Ver7.4からJSONのパーサーもDATA-INTO
組み込めるようにしている。
一応、IBMも時代に追いつこうとしているようである。
せっかくXMLを理解しかけているのにさらにまたJSON?と
思われるかも知れないがオープン系の時代の進むスピードは
速い。もう少し我慢してJSONも勉強してみよう。

JSON(=JavaScript Object Notation)はJavaScriptのような
記述のデータ形式というのが発祥であるので

・JavaScript でサーバーとのデータ交換
・IBM WatsonのようなAIとのデータ交換
・LINE が行うサーバーとのメッセージ交換

のような場面で使われている。
JSONの説明は「109.JSON とは」が最もわかりやすい。
物理ファイルとXMLとJSONの比較をもう一度確認してみよう。

・物理ファイル

SHCODE HNAME SHTANK SHSCOD
NV-CF1 Cカセット編集ビデオ 58000 0002

・XML表現

 
  <RECORD>
    <SHCODE>NV-CF1</SHCODE>
    <SHNAME>Cカセット編集ビデオ</SHNAME>
    <SHTANK>58000</SHTANK>
    <SHSCOD>0002</SHSCOD>
  </RECORD>
    :

・JSON表現

  {
    {"SHCODE": "NV-CF1",
     "SHNAME": "Cカセット編集ビデオ",
     "SHTANK": "58000",
     "SHSCOD": "0002"
    }
    :
  }

このように見ているとXMLとJSONには1対1の互換性があることがわかる。
IBM iにはXML-INTOでXMLならばデータ構造(DS=Data Structure)に変換できたのだから
JSONもXMLに変換すればやはりXML-INTOを使ってデータ構造に変換できるはずである。
そこで今回紹介するのがJSONからXML-INTOを使ってデータ構造に変換する
サンプルRPGである。

[ JSONをデータ構造に変換するRPG : TESTJSON1 ]

ソースはこちらから

0001.00 H DFTNAME(TESTJSON1) DATEDIT(*YMD/) BNDDIR('QC2LE')                     
0002.00 H CCSID(*GRAPH:*SRC)                                                    
0003.00 F********** JSON パーサーのテスト ************************************* 
0004.00 F*                                                                      
0005.00 F********************************************************************** 
0006.00                                                                         
0007.00  * CRTBNDRPG  OBJ(OBJLIB/TESTJSON1) SRCFILE(MYSRCLIB/QRPGLESRC)         
0008.00  * DFTACTRP(*NO) ACTGRP(*NEW) DBGVIEW(*SOURCE) AUT(*ALL)                
0009.00                                                                         
0010.00  *-------------------------------------------------------------------*  
0011.00  *  2021/11/30 : 作成                                                   
0012.00  *-------------------------------------------------------------------*  
0013.00  ****************************************************                   
0014.00  *       プロシージャーのプロトタイプ宣言           *                   
0015.00  ****************************************************                   
0016.00 D*( JSON2XML のプロトタイプ宣言 )                                       
0017.00 D JSON2XML        PR            10I 0                                   
0018.00 D  JSON_P                         *   Value                             
0019.00 D  XML_P                          *   Value                             
0020.00                                                                         
0021.00  *( 作業変数 )                                                          
0022.00 D TRUE#           S             10I 0 INZ(0)                            
0023.00 D FALSE#          S             10I 0 INZ(-1)                           
0024.00 D JSON            S            512A                                   
0025.00 D XML             S            512A                                   
0026.00 D UCS2            S            512C   CCSID(1200)                     
0027.00                                                                       
0028.00 D RECORD          DS                  QUALIFIED                       
0029.00 D   SHCODE                1     10A                                   
0030.00 D   SHNAME               11     34A                                   
0031.00 D   SHTANK               35     41S 0                                 
0032.00 D   SHSCOD               42     45A                                   
0033.00                                                                       
0034.00  /FREE                                                                
0035.00    JSON = '{+                                                         
0036.00              "SHCODE": "NV-CF1",+                                     
0037.00              "SHNAME": " Cカセット編集ビデオ ",+                     
0038.00              "SHTANK":"58000",+                                       
0039.00              "SHSCOD":"0002"+                                         
0040.00           }';                                                         
0041.00    JSON2XML(%ADDR(JSON): %ADDR(XML)); //JSON を XML に変換する        
0042.00    UCS2 = %UCS2(XML);                                                 
0043.00    XML-INTO RECORD %XML(UCS2:'ccsid=ucs2 case=any');                  
0044.00  /END-FREE                                                            
0045.00 C                   SETON                                        LR   
0046.00 C                   RETURN                                            
0047.00  ************************************************************         
0048.00  *   JSON2XML        : JSON を XML に変換する                      
0049.00  ************************************************************      
0050.00  *---( JSON2XML                  ここから )-----------------------*
0051.00  * JSON を XML に変換する                                          
0052.00 P JSON2XML        B                                                
0053.00 D                 PI            10I 0                              
0054.00 D  JSON_P                         *   Value                        
0055.00 D  XML_P                          *   Value                        
0056.00  *                                                                 
0057.00 D JSON            S              1A   BASED(JSON_P)                
0058.00 D                                     DIM(512)                     
0059.00 D XML             S            512A   BASED(XML_P)                 
0060.00 D N               S              4S 0                              
0061.00 D LEN             S              4S 0                              
0062.00 D X               S              4S 0                              
0063.00 D FLD             S             10A                                
0064.00 D VALUE           S            256A                                
0065.00 D bFLD            S               N   INZ(*OFF)                    
0066.00 D bVALUE          S               N   INZ(*OFF)                    
0067.00 D bOE             S               N   INZ(*OFF)                    
0068.00 D OE              S              1A   INZ(X'0E')                   
0069.00 D OF              S              1A   INZ(X'0F')                   
0070.00                                                                    
0071.00  /FREE                                                             
0072.00    X = 0;                                                                         
0073.00    LEN = %ELEM(JSON);                                                             
0074.00    FOR N = 1 TO LEN;                                                              
0075.00      SELECT;                                                                      
0076.00      WHEN  JSON(N) = '{';                                                         
0077.00            XML = '';                                                      
0078.00      WHEN  JSON(N) = '"';                                                         
0079.00            IF bOE  = *ON;                                                         
0080.00              VALUE = %TRIM(VALUE) + JSON(N);  // 漢字中                           
0081.00              ITER;                                                                
0082.00            ENDIF;                                                                 
0083.00            IF bFLD = *OFF;                                                        
0084.00               IF %LEN(%TRIMR(FLD)) > 0;                      // フィールドの終わり
0085.00                  bFLD = *ON;                                                      
0086.00                  XML = %TRIMR(XML) + '<' + %TRIMR(FLD) + '>';                     
0087.00               ENDIF;                                                              
0088.00            ELSE;                                                                  
0089.00               IF bVALUE = *OFF;                                                   
0090.00                 IF %LEN(%TRIMR(VALUE)) > 0;                                       
0091.00                   bVALUE = *ON;                              // 値の終わり        
0092.00                   XML = %TRIMR(XML) + %TRIMR(VALUE) +                             
0093.00                         '';                                 
0094.00                 ENDIF;                                                            
0095.00               ELSE;                                                               
0096.00               ENDIF;                                       
0097.00            ENDIF;                                          
0098.00      WHEN  JSON(N) = ':';                                  
0099.00            IF bOE  = *ON;                                  
0100.00              VALUE = %TRIM(VALUE) + JSON(N);  // 漢字中    
0101.00              ITER;                                         
0102.00            ENDIF;                                          
0103.00      WHEN  JSON(N) = ',';                                  
0104.00            IF bOE  = *ON;                                  
0105.00              VALUE = %TRIM(VALUE) + JSON(N);  // 漢字中    
0106.00              ITER;                                         
0107.00            ENDIF;                                          
0108.00            bFLD   = *OFF;                                  
0109.00            bVALUE = *OFF;                                  
0110.00            FLD    = ' ';                                   
0111.00            VALUE = ' ';                                    
0112.00      WHEN  JSON(N) = '}';                                  
0113.00            IF bOE = *OFF;                                  
0114.00              XML = %TRIMR(XML) + '';              
0115.00            ELSE;                                           
0116.00              VALUE = %TRIM(VALUE) + JSON(N);  // 漢字中    
0117.00            ENDIF;                                          
0118.00      OTHER;                                                
0119.00            IF JSON(N) = OE;                                
0120.00              bOE = *ON;                                             
0121.00            ELSE;                                                    
0122.00              IF JSON(N) = OF;                                       
0123.00              bOE = *OFF;                                            
0124.00              ENDIF;                                                 
0125.00            ENDIF;                                                   
0126.00            IF bFLD = *OFF;                                          
0127.00                FLD = %TRIMR(FLD) + JSON(N);                         
0128.00            ELSE;                                                    
0129.00              VALUE = %TRIM(VALUE) + JSON(N);                        
0130.00            ENDIF;                                                   
0131.00      ENDSL;                                                         
0132.00    ENDFOR;                                                          
0133.00  /END-FREE                                                          
0134.00 C                   RETURN                  TRUE#                   
0135.00 P                 E                                                 
0136.00  *---( JSON2XML                  ここまで )----------------------*  



[コンパイル]

CRTBNDRPG PGM(TEST.COM/TESTJSON2) SRCFILE(R610SRC/QRPGLESRC) DFTACTGRP(*NO)
ACTGRP(*NEW) DBGVIEW(*SOURCE) AUT(*ALL)

[解説]

この例では最も簡単なJSONとして

0035.00    JSON = '{+                                                         
0036.00              "SHCODE": "NV-CF1",+                                     
0037.00              "SHNAME": " Cカセット編集ビデオ ",+                     
0038.00              "SHTANK":"58000",+                                       
0039.00              "SHSCOD":"0002"+                                         
0040.00           }';

を用意してそれを

0041.00    JSON2XML(%ADDR(JSON): %ADDR(XML)); //JSON を XML に変換する

によってJSONをJSON2XML というプロシージャーでXMLに変換している。
JSON2XMLはC言語で書いたほうがシンプルでわかりやすいのだが
C言語のサービス・プログラムを書くと大規模になってしまうのと
読者はRPGのほうがわかりやすいだろうとの考慮からRPGで
プロシージャーを記述した。
次にXMLの変数: XML に変換して

0042.00    UCS2 = %UCS2(XML);

によって EBCDIC:XMLを UTF-16: UCS2 に変換する。
これは先に説明したようにXML-INTOは日本語環境のCCSID: 5026, 5025および 1399をサポートしていない
ので唯一互換性のあるUnicodeのUTF-16に変換してから

0043.00    XML-INTO RECORD %XML(UCS2:'ccsid=ucs2 case=any'); 

でデータ構造

0028.00 D RECORD          DS                  QUALIFIED                       
0029.00 D   SHCODE                1     10A                                   
0030.00 D   SHNAME               11     34A                                   
0031.00 D   SHTANK               35     41S 0                                 
0032.00 D   SHSCOD               42     45A  

に変換する。
STRDBG で XML-INTOの実行後に停止させて EVAL RECORD でデータ構造の中身を参照すると

                                    評価式                                 
                                                                           
 前のデバッグ式                                                            
                                                                           
 > EVAL record                                                             
   RECORD.SHCODE = 'NV-CF1    '                                            
   RECORD.SHNAME = ' Cカセット編集ビデオ   '                              
   RECORD.SHTANK = 0058000.                                                
   RECORD.SHSCOD = '0002'                                                  
                                                                           

のようにデータ構造: RECORD の中にJSONの値が更新されていることが確認できる。
このようにして私たちはJSONに本質的に同じ意味を持つデータ構造(DS)にデータを
写すことができたのである。
まず最初の第一歩としてJSONを扱うことができるようになった。
このAS400 TIPS & Techniques もXML-INTO の紹介に始まってJSONも解説しているのだから
十分時代に沿っているはずである。
レガシーなテクニックではなく時代の先端を行こうとしている。
米国の記事を書き写しただけの紹介ではない。

RPGフリー・フォーマット

ところで上記のRPGソースの演算はほとんどがフリー・フォーマットで書かれている。
フリー・フォーマットではなく固定形式で記述すると読み手は読むのも嫌になるような
ロジックのなるがご覧のようにフリー・フォーマットであれば
他人のソースであってもわかりやすくなる。
なぜフリー・フォーマットが優れているかについては来年の技術ショート・セミナーで
解説を予定している。お申し込みは12月17日より開始の予定。