DATA-INTOにひと工夫加えてJSONを解析する
最近、関わったプロジェクトで、Webサービスから返されるJSONを解析することが必要になりました。一見、非常に簡単なタスクのように思われましたが、すぐに難問にぶち当たりました。返されるJSONにはトップレベル要素がなく、JSONをデータ構造へロードしたかったのですが、私のプログラムでは実行できませんでした。
少し調べてみたところ、選択肢として、パーサー(JSONPARSE)でコードを変更する方法と、まったく別のパーサーを使用する方法があることが分かりました。これらは両方とも実行可能な選択肢ですが、多少異なったやり方を取ってみることにしました。
そのやり方を説明する前に、JSONがどのようなものなのかを示しておいた方がよいでしょう(図1)。
この記事で使用されているコードは、 ここからダウンロードできます。DATA-INTOをデータ構造で使用するときは、データ構造のすべての要素が、データ構造にロードされるJSONと完全に一致する必要があります。データ構造を作成したら、データ構造およびその中の変数それぞれに名前を付ける必要があります。けれども、ご覧の通り、私のJSONには、トップレベル要素がありません。ここで問題が生じます。データ構造のトップレベルに名前を付ける必要があり、また、それはJSONのトップレベルに一致する必要があります。そのため、JSONを格納するデータ構造を作成しました(図2)が、DATA-INTOは失敗してしまいます。これは、データ構造内の変数がJSONオブジェクトと完全に一致しないためです(トップレベルの「itemDs」で失敗します)。
以下のコードは、JSONを格納するデータ構造を示しています。
dcl-ds itemDs Qualified;
success char(5);
classdesc char(30);
numberofitems char(5);
dcl-ds itemlist dim(50) inz;
itemnumber char(5);
description char(30);
unitofmeasure char(5);
itemclass char(5);
end-ds itemlist;
end-ds itemDs;
では、パーサーを修正するでも、別のパーサーを使用するでもなく、どのようにして、この難問を解決したのでしょうか。その答えは、Webサービスから返されるJSONにトップレベル要素を追加することです。なるほど、それはよさそうですが、どのようにして行うのでしょうか。
私はWebサービスを利用するためにSQL関数HTTPGETCLOBを使用しているため、JSON結果をJSONオブジェクト内にラッッピングするだけです。これでJSONオブジェクトが最高レベルになり、データ構造(itemDs)のトップレベルに一致するように名前を付けます。次のコードは、HTTPGETCLOBと連結されたJSONオブジェクトHTTPGETCLOBを示しています。
// since the highest level of the json array isn't named, we need to
// apply one to it so we can have it match the data structure we're
// loading the json into. That is done by concatenating ' "itemDs" : '
// with the HttpGetClob function.
Exec Sql
Select '{ "itemDs" : ' concat Systools.HttpGetClob
(:WebServiceUrl , :WebServiceHeaderJson) concat '}'
into :itemJson
from sysibm.sysdummy1;
JSONオブジェクトの用意ができたら、DATA-INTOと作成したデータ構造を使用して解析を行うことができます。
Data-into itemDs %Data(%trim(itemJson):
'case=any allowextra=yes')
%Parser('JSONTSTLIB/JSONPARSE');
以前、SQL関数JSON_TABLEを使用する際に、この手法を使用したことがありましたが、同じように機能します。
多くの場合、問題にはさまざまな解決策があります。ここで紹介した手法が、同じような状況に直面した際のもうひとつの選択肢となるとよいと思います。Webサービスを利用し、返されたJSONを解析するためのコード一式は、ダウンロードしてご利用いただけます。