2018.08.09


Jon Paris著

RPGの新たなDATA-INTO

この記事では、RPG言語への最新の追加機能、すなわち新たなDATA-INTO命令コードについて簡単に紹介します。DATA-INTOは、よくIBMに寄せられる「XMLと同じくらい簡単にJSONを処理できるように、JSON-INTOが導入されるのはいつ頃になる予定ですか」という質問に対する、IBMからの回答ということのようです。

DATA-INTOはこの機能を提供しますが、IBMは、単純なJSON-INTO命令コードがこれまで実行できていた機能を越える機能を、うまい具合にDATA-INTOに加えています。DATA-INTOは、実際上、XML-INTOとOpen Accessとの中間的な存在と言えます。XML-INTOと同様に、DATA-INTOはアイテムの名前およびそれらの階層を使用して、ドキュメントをRPG変数へアンパックします。また、Open Accessと同様に、DATA-INTOはほとんどすべての形式の構造化データを処理できる拡張可能なフレームワークを提供します。CSVファイル、プロパティ ファイル、そして(もちろん)JSONに適用できます。おそらく将来的により重要なことに、DATA-INTOは、次にどのような「大物」の構造化データが登場してきても、すぐにそれに適応できます。

そうできるのは、生のデータを解析するパーサーをユーザーが自分で用意するからです。とは言っても、ユーザーが自分でパーサーを書かなければ、この新機能を使用できないというわけではありません。IBMがいくつかのサンプル パーサーを提供しており、他にもサード パーティから利用可能なものもあるからです。詳細については後述します。

この記事で使用されているコードは、 ここからダウンロードできます

基本的な構文は、XML-INTOを使用してきた方にとっては非常に馴染み深いものに思えるはずです。以下は、後で説明するプログラムからのサンプル コードです。

DATA-INTO orders %Data( jsonData: 'case=any')
                          %Parser('*LIBL/JSONPARSE');

1つ目のパラメーター(orders)は、命令のターゲット、すなわち、命令の結果が入れられる変数を識別します。通常、これはデータ構造またはデータ構造配列となります。処理されているドキュメントのコンテンツをターゲットとなるデータ構造へどのようにRPGがマッピングするかについては、後でサンプル プログラムについて説明するときに見て行くつもりです。

2つ目のパラメーター(%DATA)は、解析されるデータのソース(jsonData)と、適用される処理オプション(‘case=any’)を指定します。DATA-INTOは、XML-INTOからのすでにお馴染みかもしれない同じ処理オプションを使用します。これらのオプションのいくつかについては、サンプルを見て行くときに説明します。

3つ目のパラメーター(%PARSER)は、構文解析命令を実行するプログラムまたはサブプロシージャーを識別します。このBIFには、パーサー固有のパラメーターを渡すことを可能にする2番目のパラメーター(オプション)があります。たとえば、このプログラムで使用しているIBM提供のJSONパーサーには、デバッグ情報を制御する「diagMessages」と、JSON true/false値がどのように処理されるかを制御する「boolean」という、利用可能な2つのパラメーターがあります。

IBM提供のパーサーについて言えば . . .、IBMは、3つのパーサーのソース コード(すべてRPGで書かれている)を提供しています。1つ目のパーサーは、多くのユーザーが待ち望んでいたものであり、これはJSONを解析します。パーサーの名前はJSONPARSEで、簡潔にするために、このサンプルではこのパーサーを使用しています。ただし、これは製品グレードのパーサーとして意図されたものではないことを指摘しておいたほうがよいでしょう。これは、パーサーの書き方の一例としてIBMが提供しているものです。

2つ目と3つ目のパーサーは、プロパティ ファイル パーサーのバリエーションです。「プロパティ ファイル」というのは、DATA-INTO独自の処理オプションなど、キーワード/値のペアで構造化されているデータのことで、たとえば、‘case=any’などです。1つのパーサーは、それぞれのプロパティが別々の行にあるプロパティ ファイルを処理するように設計されています。もうひとつのパーサーは、値のペアが文字(通常、セミコロン)で区切られているプロパティを処理するように設計されています。たとえば、key1=value1 ; key2=value2 ; など … です。

サンプル プログラムを理解する

解析されるJSONファイルは、特定の顧客によって注文された品目のコードと数量の詳細情報が入った、簡略版のordersドキュメントから成ります。以下は、ドキュメントから抽出したものを、構造が見やすいように「すっきりした」形式にしたものです。Ordersドキュメントの後でそれぞれの行について説明します。

(A) { "Orders" : [
(B)   { "Customer" : "B012345", "Items" : [
(C)       { "Code" : "12-345", "Quantity" : 120 },
          { "Code" : "12-678", "Quantity" : 10 } ] }, 
(D)   { "Customer" : "C123456", "Items" : [
          { "Code" : "23-456", "Quantity" : 50 },
           ...

(A) ドキュメントを開始し、ドキュメントがOrdersを含んでいるものと確認します。OrdersはJSON配列によって表現されるため、配列の先頭は「[」で示されます。

(B) これはドキュメント内の1つ目の注文であり、必須のCustomerおよびItemsのリスト(配列)を識別します。

(C) 個々の品目を表示し、必須のItem CodeおよびQuantityから成ります。この行はその注文に含まれている品目の数の分だけ繰り返されます。

(D) 次のCustomerの先頭をマーキングします。このCustomerの行とItemの行の組み合わせは、ドキュメント内のすべての注文に対して繰り返されます。

JSONがどのようなものか分かったところで、最初の作業として、データ構造を作成してそのデータをマッピングします。DATA-INTOは、XML-INTOと同様に、名前および階層を用いてマッピングを行います。ordersデータ構造(下のE)から始まりますが、複数の注文が含まれているため、配列DSとして指定します。サイズはユーザーが決めますが、このサンプルでは、1つのドキュメント内の最大注文数は99件としています。処理される実際の件数は、XML-INTOと同様に、プログラム状況データ構造(PSDS)の372桁目から始まるRPGの8バイト整数に設定されます。

このDS内の1つ目のフィールドはcustomer(F)であり、品目を格納する入れ子になった配列DS(G)が後に続きます。このケースでは、1つの注文当たりの最大品目数は20個としています。

注文の行本体(H)は、必須の品目コードとそれに続く数量から構成されます。

フィールドおよびDSの名前がJSONドキュメント内での名前と完全に一致し、同じ階層レベルにあることに注目してください。つまり、ordersにはcustomerとitemsが含まれています。次いで、itemsにはコードcodeとquantityが含まれています。

(E)    dcl-ds  orders Dim(99) Qualified;
(F)       customer     char(7);
(G)       dcl-ds  items  Dim(20);
(H)          code      char(6);
             quantity  packed(5);
          end-ds  items;
       end-ds orders;

このデータ構造は目的を果たすでしょうが、DATA-INTOオプションの「allowmissing=yes」を指定することが必要になります。このオプションは常に危険なオプションであり、できれば使用を避けるべきです(そう述べる理由をご存じでない場合は、XML-INTOについて記したこちらの記事をお読みになることをお勧めします。このオプションについての説明を記しています)。そのため、allowmissingを使用する代わりに、フィールドcount_itemsをDSに追加し、すぐ下に示すように、%DATA BIFのパラメーターとしてオプションcountprefix=count_を追加しています。結果としてRPGは、それぞれの顧客エントリーごとに見つかった品目要素の数を数え、また、最大数の20品目でないどの注文もエラーとはみなしません。

以下は、プログラムが使用するDSの修正版です。

      dcl-ds  orders Dim(99) Qualified;
          customer     char(7);
(I)       count_items  int(5);
          dcl-ds  items  Dim(20);
             code      char(6);
             quantity  packed(5);
          end-ds  items;
       end-ds orders;

これでデータ構造が準備できたので、DATA-INTO命令をコーディングしてドキュメントを処理することができます。以下のようになります。

       DATA-INTO orders  %Data( jsonData:
                                'case=any countprefix=count_')
                         %Parser('QOAR/JSONPARSE');

orders DS配列は、抽出されるデータのターゲットとして識別されます。次の%DATAは、JSONのソースを識別します。この例では、変数jsonDataに格納されており、使用される処理オプションはcase=anyおよびcountprefix=count_です。最後に大事なこととして、%PARSERは、実際の構文解析命令を実行するプログラムを識別するのに使用します。このケースでは、IBM提供のサンプルのJSONパーサーであるJSONPARSEであり、私はそれをQOARライブラリーにコンパイルしました。

サンプル プログラムを試してみる

サンプル プログラムの残りの部分は非常に単純ですので、すべてについて詳細に説明するよりも、ダウンロードしてご自分で試してみる方がお勧めです。もちろん、前提として、システムに適正なレベルのPTFが適用されていること、そして提供されたソースからJSONPARSEをコンパイルしていることが必要です。必要とされるPTFの詳細については、RPG Cafeで確認することができます。

「サンプル プログラムを試している」うちに、いくつかの別のJSONドキュメントで試してみたくなるかもしれません。しかし、一部のコードを変更しないとIBMパーサーが処理しない一定のJSONのスタイルがあることに注意してください。たとえば、JSONでは、外部コンテナー(この例ではOrders配列)の名前に注意が向けられないことはよくあることです。次のように、名前の付いていない配列から始まるドキュメントがあったとします。

{   [  { "Customer" : "B012345", "Items" : [ ...

そのままの状態では、IBMのJSONパーサーはこれを処理することができません。このパーサーは、XMLドキュメントでそうであるように、トップ レベルには名前が付いていることを求めます。そのようなドキュメントの場合は、JSONPARSEに対して多少のコード変更を行う必要があります。ただし、もっと良い方法もあります。それは、Scott Klement氏のYAJLINTOパーサーを使用することです。YAJLINTOパーサーは、同氏のWebサイトに置かれているYAJL JSONパッケージに含まれています。

Scott氏のパーサーとそのオプション、およびDATA-INTOに関するいくつかの追加情報については、次回の記事で紹介するつもりです。

追伸。現時点では、DATA-INTO用のパーサーの「書き方」を説明する記事を執筆する予定はありませんが、DATA-INTOアプローチのメリットを十分に引き出せるようなタイプのデータをお持ちであり、そのようなパーサーを設計・構築するにはどのようにしたらよいかお知りになりたいという場合には、どうぞコメント欄を通じてその旨をお知らせください。

ページトップ

ボタン