はてなブックマークエントリー取得 API は、JSON で結果を返却します。
そのため、以下のようなパラメータをコンストラクタを渡せば、一件問題がないように見えます。
my $ws = WebService::Simple->new( base_url => 'http://b.hatena.ne.jp/entry/jsonlite/', response_parser => 'JSON', );
しかし、当該の API は、「誰もブックマークしていない」URL に対しては JSON のオブジェクトではなく、"null" という文字列を返却してきます。
上記の形で生成した WebService::Simple オブジェクトは、この "null" を正しくパースできず、"JSON text must be an object or array (but found number, string, true, false or null, use allow_nonref to allow this) " というエラーを吐いてしまいます。
これは、WebService::Simple::Parser::JSON が内部で使用する JSON モジュールは、"JSON text" のパースを行うためです。JSON text は RFC4627 上では object または array として定義されており、いわゆる文字列は JSON text の範疇には含まれません。結果として JSON モジュールがパースに失敗し、上記のエラーメッセージを吐いています。
実際に、JSON.pm の POD にも以下の記述があります。
There is no guessing, no generating of illegal JSON texts by default, and only JSON is accepted as input by default (the latter is a security feature).
では、JSON モジュールにどうやって "null" をパースさせるようにすれば良いかというと、エラーメッセージに書いてある通り、allow_nonref を呼び出してやれば良いです。
my $json = JSON->new->allow_nonref
これによって、$json は null をパースできるようになります。
次の問題は、"null" をパースできるようになった上記の $json を、パーサとして WebService::Simple に使わせるにはどうすれば良いかという問題です。
結論から言うと、以下の方法で良いです。
my $ws = WebService::Simple->new( base_url => 'http://b.hatena.ne.jp/entry/jsonlite/', response_parser => WebService::Simple::Parser::JSON->new(json => JSON->new->allow_nonref), );
WebService::Simple の response_parser パラメータには、WebService::Simple::Parser の子クラスのオブジェクトを直接渡すことが可能です。また、WebService::Simple::Parser::JSON のコンストラクタは、引数として json パラメータを取ることができ、そこに JSON モジュールのオブジェクトを渡せば、内部で当該のオブジェクトが使用される作りになっています。従って、上記の形で WebService::Simple オブジェクトを作成すれば、"null" が返却されてもエラーとなることはなくなります。
あとは、以下のようなかんじでよいのではないでしょうか。エラー処理は別途必要ですが。
my $res = $ws->get({ url => $url })->parse_response;
まぁそのまえに、
- GET で叩くだけなんだから UserAgent とか Furl とかそのまま使えばよくね?
- content が "null" かどうかをチェックした後でパースするようにすれば良くね?
という話はある。