Ajax の弱点は別ドメインのページを取得できないこと。そんな制限を取っ払って、別ドメインの XML を取得できる CGI を作りました。
任意のドメインにある XML ファイルを取得できるできる外部 JavaScript です。取得したい XML の URL をパラメータにして CGI を外部JavaScriptファイル としてインポートして利用します。
簡易 RSS リーダをあなたの blog に貼り付けることだってできちゃいます。
技術的な補足をすると...
- JKL.ParseXML のクロスドメイン版と考えてください。
- JSONの書き方について考えてみた に影響をうけてます。
- いちおうJSONP(JSON with Padding) という名前がついているようです。
なお、drk7.jp さんが同様のサービスを開始しています。
drk7.jpさんのほうが実績も信用もあると思いますので、そちらをお勧めします(2006.3.10追記)。
使い方
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ファイルへのアクセス方法1(同期)
XML の URL は http://www.example.com/sample.xml だと仮定します。XML を読み込むには次のようなタグを HTML に記述します。
<script charset="utf-8" type="text/javascript" src="http://tech.nitoyon.com/xml2json.cgi?url=http%3A%2F%2Fwww.example.com%2Fsample.xml"></script>
上記の 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 の情報を利用します。
<script charset="utf-8" type="text/javascript" src="http://tech.nitoyon.com/xml2json.cgi?url=http%3A%2F%2Fwww.example.com%2Fsample.xml"></script> <script> alert(xml.data["items"]["item"]["jcity"]; </script>
XMLファイルへのアクセス方法2(非同期)
script タグでインポートしてしまうと、スクリプトのダウンロードが完了するまで、script タグ以降の HTML の表示がとまってしまいます。
そこで、非同期に xml2json を呼び出す方法を示します。ここでは、window.onload イベントで読み込みを行っています。
<script> window.onload = function(){ var s = document.getElementsByTagName("head")[0].appendChild(document.createElement("script")); s.type = "text/javascript"; s.charset = "utf-8"; s.src = "http://tech.nitoyon.com/xml2json.cgi?url=http%3A%2F%2Fwww.example.com%2Fsample.xml"; } var xml = {}; xml.onload = function(data){ // 読み込み後の処理 }
デモ
XML2JSON を利用した簡易RSSリーダです。はてなブックマークの最近の人気エントリを表示しています。
ソースコード
<div id="rss_field" style="border:1px solid red; background:#f99">LOADING...</div><script> window.onload = function(){ var s = document.getElementsByTagName("head")[0].appendChild(document.createElement("script")); s.type = "text/javascript"; s.charset = "utf-8"; s.src = "http://tech.nitoyon.com/xml2json.cgi?url=http%3A%2F%2Fb.hatena.ne.jp%2Fhotentry%3Fmode%3Drss"; } var xml = {}; xml.onload = function(data){ var items = data["item"]; if(items.length == 0) return; var h = "<ul>\n"; for(var i = 0; i < Math.min(items.length, 20); i++){ var item = items[i]; if(typeof(item["description"]) == "object") item["description"] = ""; if(typeof(item["title"]) == "object") item["title"] = ""; h += "<li><a href=\"" + item["link"] + "\" title=\"" + item["description"] + "\">" + item["title"] + "</a></li>\n"; } h += "</ul>"; document.getElementById("field").innerHTML = h; } </script>
思うこと
開発者的には RSS を XML で吐き出すのと同時に JSON で吐き出してくれるページが増えてくれたらうれしいなぁ。先進的な はてな さんとかどうですか。(2006年10月現在、JSONP を利用した はてなブックマークエントリー情報取得API がリリースされています)
ただ、悪意のある JavaScript をインポートしてしまうと、訪問者の Cookie が漏れてしまうので、Cookie で個人情報を管理している場合は注意が必要です。信頼できる会社に XML2JSON なサービスをやってほしいところです。
XML2JSON のソースコード
シンプル版
分かりやすくするために、複雑なエラー処理がない単純なソースコードを示します。サーバー側で CGI として動作します。
LWP::UserAgent でダウンロードして XML::Simple で XML 解析して、Data::Dumper でダンプしています。Perl の Dump と JavaScript の JSON 形式が酷似しているため、処理はこれだけです(ダンプデータの => を : に置換するだけ・・・)。
#!/usr/local/bin/perl use CGI; use LWP::UserAgent; use XML::Simple; use Data::Dumper; my $q = new CGI; # param my $var = $q->param('var'); $var = "xml" if !defined $var or $var!~/^[a-zA-Z_]+$/; my $url = $q->param('url'); die if !defined $url or $url!~m!^http://!; # request my $ua = LWP::UserAgent->new; $ua->agent("XML2JSON/0.1 "); my $req = HTTP::Request->new(GET => $url); my $res = $ua->request($req); die if !$res->is_success; die 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); $json=~s/^\$VAR1/$var.data/; $json=~s/([^\\])" => ("|{|\[)/$1" : $2/g; # output print $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);";
キャッシュ可能版
毎回サーバーに取りに行くのは負荷が高いので、実際にはキャッシュ機能をつけています。