Webサービスは、今や一般的なアプリケーションの1つになっていますが、IBM iではJavaなどのRPG以外の言語を使用して対応するのが一般的だったのではないでしょうか。しかし、RPGにも新たにDATA-INTOやDATA-GENという命令が追加され、RPGでWebサービスを構築できるようになっています。そこでまず、第12回、第13回ではデータ交換形式のJSONについての基本的な知識と扱い方を、第14回、第15回ではDATA-INTOとDATA-GENによるWebサービスのコーディングに関する基礎知識をご提供するために翻訳解説記事をお届けすることにします。
2020/06/01 ジョン・パリス、スーザン・ガントナー
本シリーズの第一部では、JSON Webサービス要求の生成ならびに応答の処理手段として、RPGのDATA-INTOおよびDATA-GEN命令の基本的な使用法について議論しました。あなたのプログラムコードが実際にはWebサービスを提供している場合には、もちろんこれらの機能を単純に逆転させ、受信した要求を処理するのにDATA-INTOを、応答を準備するのにDATA-GENを使用します。
今回は、これらの命令コードで使用可能な追加の処理オプションのいくつかについて少々深掘りして、いつこれらを使用する必要があるか議論したいと思います。これらのオプションのいくつかはYAJLINTOパーサーおよびYAJLDTAGENジェネレーターに対してだけ適用できる(したがって、組み込み関数%PARSERおよび%GENの一部として指定される)こと、そしてその他のオプションはDATA-INTOまたはDATA-GEN命令コードそれ自身に対して適用でき、組み込み関数%DATAの一部として指定されることに注意してください。
特殊データ型に対するYAJLINTOオプション
JSONは、値が「true」(真)または「false」(偽)であるようなブーリアン・データ型(すなわち、RPG用語では標識)をサポートしています。デフォルトではYAJLINTOはこれらを「1」(真)および「0」(偽)に対応付けます。JSONの要素を標識に対応させたい場合にはこれらのデフォルト値はうまく働きます。しかし、ある場合には既存の文字フラグに対応させたいことがあるかもしれません。ことによると、条件が真であることを表すのに「Y」を、偽であることを表すのに「N」を使用したいことがあるかもしれません。そのような場合には、デフォルト値を上書きする必要があります。
これは次に示すように、組み込み関数%PARSERに対してオプション文字列を指定することで達成されます。
Data-Into customerData2 %Data( jsonData : 'case=any countprefix=num_ ' ) %Parser('YAJL/YAJLINTO' " '{ "value_true":"Y", "value_false":"N" }');
置き換えデータの長さに制限はありませんので、例えばアプリケーションの要求に従って次のように、真なら「Yes」、偽なら「No」とすることもできます。
"value_true":"Yes", "value_false":"No"
特殊な処置を必要とするJSONのもう1つの側面はヌル値です。ヌル値はJSONデータのあらゆる型に対して使用可能であり、このことからRPGでそのような文書を処理する方法を決定する際に考慮が必要になる可能性があります。
伝統的なRPGアプリケーションでは、文字フィールド内のヌル値と等価なものとして通常は空白文字が使用されます。しかし、数字フィールドではそうはいかず、0からハイバリュー、負の数までのすべてが使われるかもしれません。デフォルトでは、YAJLINTOはJSON中のヌル値に遭遇すると「*NULL」という文字列を返します。もちろん、問題はこれによってその戻り値(「*NULL」)を数字フィールドに割り当てようとするとエラーが起きることです。この問題にどう対応したら良いのでしょうか。
ヌル値になり得るJSON内の唯一の要素が数字であると分かっているなら、次に示すように目標のフィールドに-1または望みの別の値をセットするためにvalue_nullという%PARSERのオプションが使用できるかもしれません。しかし、その値は文字列として表現されなければならないことを念頭に置いてください。ですから、-1という値を使うためには次に示すようなオプションをコーディングすることになるでしょう。
%Parser('YAJL/YAJLINTO' : '{ "value_null" : "-1" }' );
しかし、違うフィールドに対して異なる値が必要ならば、あるいはJSONが様々なデータ型に対してヌル値を含み得るならば、デフォルトの設定を使用して、すべてのそのような値を最低5桁の文字長をもつ文字フィールドに対応させるのが多分良いでしょう。その後で各フィールドが「*NULL」であるか調べ、フィールドのデータ型と後続のRPGロジックでの使用法に基づいて適切な値をセットすることになります。
「不正な」RPGフィールド名の取り扱い
RPGで直接サポートされていないデータの取り扱い方法が分かったところで、JSONでは有効な要素名でありながらRPGではフィールド名として無効な名前をどう取り扱えば良いのでしょう。名前についてどのような問題があるかに応じて、これに対処する2つの方法を見てみましょう。1番目はDATA-INTOのオプションを使う方法、2番目はYAJLINTOで実装されたオプションを使う方法です。
JSONを処理する際に、JSONの要素名には(RPGでは絶対にしないことですが)空白文字が含まれるのが普通だということを覚えておくことが重要です。この問題を解決するためにDATA-INTOの支援機能があり、これはXML-INTOと同じやり方で問題を解決します。XML-INTOに馴染みのある方はそれがお分かりでしょう。
ほとんどの非RPG名を有効なRPG名に変換するために、組み込関数%DATAに対するcase=convertオプションを使います。詳細については以前の「New and Improved XML-INTO」という記事をご覧頂きたいのですが、これの簡単な例をこの後に示します。このオプションを使用する場合、有効なRPGのフィールド名ではない任意のJSONの要素名は、互換性をもたせるために変更されます。多くの場合、無効な文字は下線符号で置き換えられます。
下記の簡単な例は、これを扱うためにデータ構造(以下、DSと略記)およびDATA-INTOをコーディングする方法を示しています。JSONの要素名「customer id」の中にある空白文字に注意し、オプションcase=convertを使用することで、これがどのようにDS中のcustomer_idというフィールド名に対応付けされるかに注目してください。
dcl-s jsonData varChar(4000) inz('{ "customers": [ - { "customer id": 12345, - "name": "Paris" }, - ... ] }'); dcl-ds customerData Inz Qualified; num_customers int(5); dcl-ds customers Dim(99); customer_id Zoned(5); name Char(40); end-ds; end-Ds; Data-Into customerData %Data( jsonData : 'case=convert countprefix=num_ ' ) %Parser('YAJL/YAJLINTO');
起こり得るもう1つの名前の問題は、数字で始まるJSONの要素名です。case=convertがオンであること自体は名前を有効なものに変換するのですが、それは単に名前から先頭の数字を除去することでそうするだけです。これは常によい解決策ではないかもしれません。
そのような場合のために、YAJLINTOの%PARSER関数にはnumber_prefixオプションがあり、これによって数字で始まるJSONの任意の要素名に文字の接頭辞を付けることができ、有効なRPG名にすることができます。
ですから、%PARSERにcase=convertと一緒にオプション{ “number_prefix” : “Num_” }を指定すると、「30 day balance」というJSONの要素名はNum_30_day_balanceという有効なRPGの変数名に関連付けられます。
時々その他のオプションが必要になる場合もあるかもしれませんが、これはこれまで私たちが使ったオプションの大部分をカバーしています。
DATA-GENの%DATAオプション
さて今度はDATA-GENで使われるいくつかの処理オプションを見てみましょう。後でYAJLDTAGEN特有のオプションについて詳しく調べます。
YAJLDTAGEN特有のオプションについて詳しく調べます。countprefixオプションから始めましょう。DATA-INTOに対するオプションとして指定された場合、それは文書中で見つかったその名前をもつ要素の数をRPGが返すフィールドを識別します。ですから、合理的に推測される通り、DATA-GENでこのオプションを使用すると、名前付き要素を何個生成するかRPGに通知します。前の例のcustomerDataというDSをDATA-GENのソースとして使用し、フィールドnum_customersの値が2ならば、2つのcustomers要素を生成します。
DATA-INTOでcountprefixを使用せずに済ませられることが多くありますが、DATA-GENを使用する場合には通常これは不可欠です。その理由は、カウントを省略すると、DATA-GENは99のオカレンス(つまり、配列内の要素の数)を生成するからです。これは、このJSONを受信して使用する人からは好ましく思われません。何故なら、彼らは97個の空の要素を無視して処理をしなくてはならない事を望まないからです。
この機能のもう1つの使用法(すぐには明らかではないかもしれませんが)は、文書のオプション部分の出力を制御するために使用することです。例えば、要求が成功した場合、次のようなJSONを送信したいとします。
{ "success": true, "detail": { "customer_id": 123, "name": "Jones and Company" } }
そして、エラーが検知された場合、次のようなJSONを送信したいとしましょう。
{ "success": false, "errors": { "errorCode": "A123", "errorText": "My error message" } }
顧客レベルとエラーレベルの両方でカウントを使うことにより、これが達成できます。 次のDSで、それがどのように見えるか見当が付くでしょうし、フィールドのコメントで多分使用法が明らかになるでしょう。
dcl-ds customerData Inz Qualified; success ind; // 成功ならオン、エラーならオフ num_errors int(5); // エラーなら非ゼロに設定 dcl-ds errors; errorCode char(5); errorText varchar(60); end-ds; num_detail int(5); // 顧客エラーの場合、ゼロに設定 dcl-ds detail; customer_id Zoned(5);
もう1つのアプローチは、JSONをバラバラに構築することです。DATA-GENは一連のDATA-GEN実行の出力を結合できるようにすることでこの機能を提供します。関心が高いようであればば、この機能について今後の記事で取り上げます。
要素の名称変更
前述の通り、RPGとJSONの命名規則は違うので、renameprefixオプションを使用する必要がある場合があります。これにより、要素に対して代替名を提供できます。例えば、組み込み関数%DATA に対してrenameprefix = json_を指定すると、関連するフィールドの名前を変更するのに次のようなDSが使用できます。
dcl-ds customerData Inz Qualified; json_shipTo Varchar(15) Inz('Ship to address'); dcl-ds shipTo; json_street Varchar(14) Inz('street address'); street Char(40); city Char(30); state Char(2); zip Char(5); end-ds; end-ds;
結果のJSONは、shipToの値に対するオブジェクト名として「Ship to address」を使用し、その中でstreetフィールドの中身には「street address」という名前が付けられます。
YAJLDTAGENのPARSERオプション
名前の変更と言う話題について論じていたので、取り上げる最初のオプションはnumber_prefixです。これは、YAJLDTAGENの組み込み関数%PARSERに対する有効なオプションでもあります。しかしこの場合、生成される要素名から指定された接頭辞を削除するという逆の仕事をします。ですから、この場合のオプション{“number_prefix”: “Num_”}は、RPGの変数名の先頭から文字列Num_を削除し、切り取られた結果の名前を生成されたJSONで使用することを意味します。
もう1つの%PARSERのオプションはbeautifyです。デフォルト値は「false」(偽)ですが、「true」(真)に設定すると生成されるJSONはもっと読み易くなるように識別子と行送りを使って形式が整えられます。これはデバッグ時に役に立つオプションですが、ペイロードをできるだけ小さく保つために、実運用時には使用するべきではありません。
例: { “beautify” : true } または { “beautify” : false }
その他のオプションもあります。そして、スコット・クレメント氏はこのツールを機能拡張する度に、より多くのオプションを追加し続けています。
しかし、このシリーズ記事の話題は、WebサービスでRPGとJSONを使うことでしたから、最後にもう1つ関連するオプションについて述べるのが適切でしょう。それは「write to stdout」です。 これが「true」(真)に設定された場合、生成されたJSONは標準出力装置に書き込まれます。なぜこれが便利なのでしょうか?このコードを実行するRPGプログラムがApacheサーバーからWebサービスとして呼び出された場合、応答文書を書き出さなければならないのは標準出力に対してです。ですから、簡潔なWebサービスを提供するためにDATA-GENとYAJLDTAGENとの組み合わせが使用できます。
YAJLINTOは、ご想像通りApacheサーバーが受信するあらゆる入力要求の置き場であるstdin(標準入力)からのJSON要求を処理するための同様のオプションも提供します。ですから、DATA-INTOとDATA-GENをスコット・クレメント氏のYAJLツールと組み合わせることで、簡潔なWebサービスが構築できます。例を示す紙幅はここにはありませんが、スコット・クレメント氏のプレゼンテーション「Providing Web Services on IBM i」がこちらから入手できますので、それを参考にしてください。
まとめ
この短いシリーズが皆さんのお役に立つことを願っています。私たちの調査で明らかになった主要な機能のすべてを取り上げようと試みていますが、もしカバーしきれていないとお考えの機能やカバーして欲しい機能があればコメントでお知らせください。