投稿日:2021年7月8日
この連載の【第2回】と【第3回】で、RPGでXML、SCV、JSONなどの形式のデータを処理できるようにする新しいRPGの命令DATA-INTOをご紹介しました。しかし、DATA―INTO用の標準のパーサーはあくまで学習のためのもので、実務で遭遇するような複雑な構造をもつデータ形式を扱えるほど汎用性に富んではいません。そこで、今回はJSONデータの処理用にスコット・クレメント氏が作成した実用的なパーサーYAJLINTOをご紹介します。DATA-INTOとYAJLINTOを使うことにより、実務でJSONデータの取り扱いをRPGで容易に行えるようになります。(編集部)
JSONを処理する必要がある多くのタスクにYAJLを適用できるもう1つのもっと容易な方法があります!
2018年6月 ジョン・パリス、スーザン・ガントナー
私たちは最初に「An RPGer’s First Steps With JSON」という記事で、スコット・クレメント氏によるYAJL JSONツールキットの移植版を皆さんにご紹介しました。この記事では、JSONを生成するためにYAJLを使用する方法について説明しました。「Consuming JSON data with YAJL」という後続記事で、JSONを処理するためのYAJLの使用法について説明しました。しかし、その記事が公開されてから状況は変わり、JSONを処理する必要のある多くのタスクにさらに簡単にYAJLを適用できるようになりました。どのように? 多作のスコット・クレメント氏が、今度はYAJLツールキットにDATA-INTOパーサーを追加したのです。そして、それは見事に動作します。
YAJLINTOパーサーの使い方についての説明を始める前に、なぜこのツールキットにこの新しい機能が追加されたことをそんなに嬉しく思うのか少しお話しします。DATA-INTOをいくらか知っている人は、IBMがライブラリーQOARで提供されているDATA-INTOのサポート資料と一緒に、実際にJSONパーサーを出荷していることをご存知かもしれません。では、なぜそれを使用しないのでしょう? 簡単な答えは、IBMがこのパーサーを、XML-INTOと一緒に提供されているXMLパーサーと同じレベルの復元力を備えた「本番環境で使える」コードとして提供していないからです。
提供されたJSONパーサーは、どちらかと言えばDATA-INTOパーサーの書き方を示すための教材として使用することを意図しています。それを本番環境で使用したいなら、そうすることはできますが、処理できるJSON文書の種類に対するいくつかの制限にすぐに直面するでしょう。
YAJLINTOパーサーの説明に入りましょう。これは、これまでのところ最高のもので、あなたが投入可能などんなJSON文書でも扱うことができます。その使い方を見てみましょう。
この例で処理する文書は、次のようになります。
[ { "ID": 12345, "Active": true, "Name": "Paris", "Address": { "Street": "Main Street", "City": "Jasontown", "State": "CA", "Zip": "12345" } }, { "ID": 23456, "Active": false, "Name": "Rich", "Address": { "Street": "South Road", "City": "Hailsham", "State": "UK", "Zip": null ...
このファイルは、顧客情報を表すオブジェクトの配列で構成されています。この特定の文書には2つの主要な関心事項があります。1つ目は、要素 “Active”が真または偽のブール値であることです。2つ目は、2番目の配列要素の “Zip”エントリーの値がnullであることです。どちらもJSON文書では一般的であり、YAJLINTOはこれらを処理するための特定のオプションを提供しています。これについては後ほど説明します。しかし、最初の例ではパーサーのデフォルト処理を使うことにこだわります。
「RPGの新命令DATA-INTO: XML-INTOとOpen Accessの出会い」というDATA-INTOに関する最初の記事で触れたように、DATA-INTOの動作はXML-INTOにとてもよく似ています。そのため、DATA-INTOの基本機構を掘り下げて考えません。それよりも両者を比較してその違いに注目します。XML-INTOの動作の仕方に不案内で、ここで提供されているよりももっと詳細な情報が必要な場合は、「A Traditional Approach to a Modern Technology」を読んでください。
最初に行う必要があるのは、文書を保持するターゲットデータ構造を定義することです。それは以下の(A)から始まります。文書には複数の顧客に関する情報が含まれているので、99要素のデータ構造配列としてコーディングしています。各要素には、ID、Active、およびNameの各フィールドがあり、その後に(B)でJSON文書の”Address”オブジェクトにマップされるネストされたデータ構造Addressが続きます。Activeはブール値なので、それをここでは標識として表していることに注意してください。なぜなら、YAJLINTOのデフォルトの振る舞いは、falseとして値 “0”を、trueとして値 “1”を返すからです。
DATA-INTO命令のターゲットは配列なので、有効な要素の数をRPGがPSDS中に供給するという事実を活用できます。(C)でそれがどのように定義されているかを見ることができます。
(A) dcl-ds Customers Dim(99) Qualified; ID packed(5); Active ind; Name char(30); (B) dcl-ds Address; Street char(40); City char(30); State char(2); Zip char(10); end-ds; end-ds; (C) dcl-ds *N psds; count Int(20) Pos(372); end-ds; dcl-s jsonFile varchar(200) inz('/home/Paris/Extra/customerData1.json'); dcl-s i int(10); dcl-s wait char(1); (D) Data-into Customers %Data( jsonFile : 'doc=file case=any') %Parser( 'YAJL/YAJLINTO' ); (E) For i = 1 to count; // 結果の数分ループする Dsply ( 'Customer ' + %EditC( Customers(i).ID: 'X') + ' Active = ' + Customers(i).Active + ' Zip = ' + Customers(i).Address.Zip ); endfor; Dsply ('Processed ' + %Char(count) + ' Customers' ) ' ' wait; *InLr = *On;
ここで、DATA-INTOが兄弟のXML-INTOと明らかに異なる点は、3番目のパラメータ%PARSERです。XML-INTOは、IBMの組み込みXMLパーサーを使用するのでこのパラメータは不要です。しかしDATA-INTOでは、私たちはパーサーを提供しなければならず、使用されるプログラムまたはサブプロシージャを識別するのは%PARSERの仕事です。解析プロセスを制御するために、オプションで追加のパラメータを提供することもできます。この最初の例ではすべてデフォルト値を使用しているので、このオプションは使用されていません。これについては、この後すぐに詳しく説明します。
DATA-INTO命令が完了すると、(E)でPSDS内にある要素数(count)を使用して結果をループ処理します。出力を調べると、Activeフィールドはtrueのときは“1”、falseのときは“0”に設定されることが分かります。これらはYAJLINTOが使用するデフォルト値です。同様に、第2の顧客の郵便番号の値は、デフォルト値*NULLに設定されています。
デフォルト値の変更
前述のように、YAJLINTOパーサーは、組み込み関数%PARSERの2番目のパラメータを介して多くのデフォルト動作を変更する機能を提供します。このパラメータデータは、有効なJSON文書の形式でなければならず、次のオプションのいずれかを含めることができます。
- “value_true” – ブール値trueに使用する値を指定します。デフォルト値は、 “1”に設定されていますが、任意の文字値を指定できます。
- “value_false” – あなたはおそらくこれが何をするかを推測できます! デフォルト値は “0”です。たとえば、RPGアプリケーションで真偽値に “Y”と “N”という値が必要であるとします。これは、{“value_true”: “Y”, “value_false”: “N”}というパラメータ文字列を使用して実現できます
- “value_null” – デフォルト値は *NULLですが、場合によっては空白や*HIVALまたは*LOVALに設定することもできます。
- “skip_document_node” – デフォルト値はfalseです。これは説明が難しいので、以下の別の節で説明します。
- “document_name” – これにより、内側の要素をpath =%DATAオプションで参照できるよう外側の要素の名前を指定できます。デフォルト値はありません。
“skip_document_node”についての注釈
一部のJSON文書は、既存のXML文書を模倣するように設計されています。そのような文書を設計する際の問題は、XMLと違いJSONはルート要素の名前を使用できないということです。したがって、このレベルで名前を提供するために、JSON文書は追加の外部レイヤーでコード化されています。 例えば:
{ "Customers" : [ { "ID": 12345, ....
これをYAJLINTOで処理するには、通常このような追加のレベルを反映するためにデータ構造を変更する必要があります:
dcl-ds results Qualified; count_Customers Int(10); dcl-ds Customers Dim(99); ID packed(5); ....
もはや配列に直接ロードしていないので、それはPSDS数を使用できないということをも意味しています。このことから、同様にcountprefix処理オプションならびに関連するcount_Customers変数を追加しなければなりません。countprefixの使い方をよく知らない場合は「XML-INTO Revisited」を参照してください。
必要なデータ構造などを簡略化するため、YAJLINTOには{“skip_document_node”:true}が指定できます。これにより、パーサーは外側の文書ノードを完全に無視し、私達の元々の例で使われていたのと同じデータ構造とオプションが有効になります。実際、これはIBM提供のJSONパーサーのデフォルトの振る舞いですが、結果的に多くの標準的なJSON文書が処理できなくなります。
サンプルプログラムとJSON文書
私達のWebサイトで見つかるダウンロード可能なコードパッケージには多数のサンプルプログラムが含まれています。
DIYAJLとCustomerData1.jsonは、この記事の中の基本例で使用されているファイルです。
DIYAJL2とCustomerData2.jsonは、skip_document_nodeオプションを使用せずにコードを作成する方法を示すために使用されています。プログラムDIYAJL2Aがその使用方法を示しています。これらのプログラムは、他のYAJLINTO解析オプションの使用方法も示しています。
まとめ
お分かりのように、YAJLINTOとRPGのDATA-INTO命令の組み合わせは、JSON文書の処理を行うための強力かつ簡素な手法を提供します。遭遇するすべてのJSON処理要件は処理できないかもしれませんが、大方のケースを処理できることは確かです。
【編集部注】
IBM i用のYAJLはhttp://scottklement.com/yajl/ にある「YAJL for IBM i」というパッケージのダウンロードファイルへのリンク(YAJL.zip)からダウンロードできます。