Ajax の弱点は別ドメインのページを取得できないこと。そんな制限を取っ払って、別ドメインの XML を取得できる CGI を作ってみました。
XMLファイルの例:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><items><item><jcity>千代田区</jcity><jlocal>千代田</jlocal><jpref>東京都</jpref><pref_cd>13</pref_cd><zip_cd>1000001</zip_cd></item></items>XML の URL はhttp://www.example.com/sample.xml だと仮定します。XML を読み込むには次のようなタグを HTML に記述します。
<scriptcharset="utf-8"type="text/javascript"src="xml2json.cgi?url=http%3A%2F%2Fwww.example.com%2Fsample.xml"></script>xml2json.cgi は自分のサーバーに設置します。
上記の JavaScript は次のように展開されてインポートされます。
if(typeof(xml)=='undefined')xml={};xml.data={items:{item:{jcity:"千代田区",jlocal:"千代田",jpref:"東京都",pref_cd:"13",zip_cd:"1000001"}}}if(typeof(xml.onload)=='function')xml.onload(xml.data);たとえば、HTML からは次のようにして XML の情報を利用します。
<scriptcharset="utf-8"type="text/javascript"src="xml2json.cgi?url=http%3A%2F%2Fwww.example.com%2Fsample.xml"></script><script>alert(xml.data["items"]["item"]["jcity"];</script>script タグでインポートしてしまうと、スクリプトのダウンロードが完了するまで、script タグ以降の HTML の表示がとまってしまいます。
そこで、非同期に xml2json を呼び出す方法を示します。ここでは、window.onload イベントで読み込みを行っています。
window.onload=function(){vars=document.getElementsByTagName("head")[0].appendChild(document.createElement("script"));s.type="text/javascript";s.charset="utf-8";s.src="xml2json.cgi?url=http%3A%2F%2Fwww.example.com%2Fsample.xml";}varxml={};xml.onload=function(data){// 読み込み後の処理}分かりやすくするために、複雑なエラー処理がない単純なソースコードを示します。サーバー側で CGI として動作します。
LWP::UserAgent でダウンロードしてXML::Simple で XML 解析して、Data::Dumper でダンプしています。Perl の Dump と JavaScript の JSON 形式が酷似しているため、処理はこれだけです(ダンプデータの=> を: に置換するだけ・・・)。
#!/usr/local/bin/perluseCGI;useLWP::UserAgent;useXML::Simple;useData::Dumper;my$q=newCGI;# parammy$var=$q->param('var');$var="xml"if!defined$varor$var!~/^[a-zA-Z_]+$/;my$url=$q->param('url');dieif!defined$urlor$url!~m!^http://!;# requestmy$ua=LWP::UserAgent->new;$ua->agent("XML2JSON/0.1");my$req=HTTP::Request->new(GET=>$url);my$res=$ua->request($req);dieif!$res->is_success;dieif$res->headers->header("Content-Type")!~m!/xml!;# XML Dump$XML::Simple::PREFERRED_PARSER='XML::Parser';my$xmlobj=XMLin($res->content);$Data::Dumper::Indent=1;$Data::Dumper::Useqq="utf8";$json=Dumper($xmlobj);$json=~s/^\$VAR1/$var.data/;$json=~s/([^\\])" => ("|{|\[)/$1" : $2/g;# outputprint$q->header(-type=>"text/plain",-charset=>"utf-8");print"if (typeof($var) == 'undefined')$var = {};\n";print$json."\n";print"if (typeof($var.onload) == 'function')$var.onload($var.data);";毎回サーバーに取りに行くのは負荷が高いので、こんな風にキャッシュ機能をつけるとよいかもしれません。
#!/usr/local/bin/perluseCGI;useJcode;useLWP::UserAgent;useXML::Simple;useDigest::MD5qw(md5_hex);useData::Dumper;my$cache_dir="../cache/";my$cache_lifetime=600;# 10minmy$q=newCGI;# parammy$var=$q->param('var');$var="xml"if!defined$varor$var!~/^[a-zA-Z_]+$/;my$url=$q->param('url');&error("url is not set")if!defined$urlor$url!~m!^http://!;my$json;my$cache=$cache_dir.md5_hex($url);if(-f$cache&&(stat($cache))[9]+$cache_lifetime>time){open(CACHE,$cache);$json=join("",<CACHE>);close(CACHE);}else{# requestmy$ua=LWP::UserAgent->new;$ua->agent("XML2JSON/0.1");my$req=HTTP::Request->new(GET=>$url);my$res=$ua->request($req);&error("cannot get url")if!$res->is_success;&error("cotnent is not xml (".$res->headers->header("Content-Type").")")if$res->headers->header("Content-Type")!~m!/xml!;# XML Dump$XML::Simple::PREFERRED_PARSER='XML::Parser';my$xmlobj=XMLin($res->content);$Data::Dumper::Indent=1;$Data::Dumper::Useqq="utf8";$json=Dumper($xmlobj);# Write cachemy$cache=$cache_dir.md5_hex($url);if(open(CACHE,">$cache")){printCACHE$json;close(CACHE);}}$json=~s/^\$VAR1/$var.data/;$json=~s/([^\\])" => ("|{|\[)/$1" : $2/g;# outputprint$q->header(-type=>"text/plain",-charset=>"utf-8");print"if (typeof($var) == 'undefined')$var = {};\n";print$json."\n";print"if (typeof($var.onload) == 'function')$var.onload($var.data);";suberror{print$q->header(-type=>"text/plain",-charset=>"utf-8");print"var$var =\"$_[0]\";";exit;}開発者的には RSS を XML で吐き出すのと同時に JSON で吐き出してくれるページが増えてくれたらうれしいなぁ。
ただ、悪意のある JavaScript をインポートしてしまうと、訪問者の Cookie が漏れてしまうので、Cookie で個人情報を管理している場合は注意が必要です。信頼できる会社に XML2JSON なサービスをやってほしいところです。
(追記) Yahoo! Pipes を使えば、外部サイトの内容を JSONP で取得できるようになりました。詳しくはYahoo! Pipes の Page Fetch モジュールでスクレイピングし放題 を見てください。