JavaScript

xml2json.cgi - ドメインを超えてXMLを読みこむ

Ajax の弱点は別ドメインのページを取得できないこと。そんな制限を取っ払って、別ドメインの XML を取得できる CGI を作りました。

任意のドメインにある XML ファイルを取得できるできる外部 JavaScript です。取得したい XML の URL をパラメータにして CGI を外部JavaScriptファイル としてインポートして利用します。

簡易 RSS リーダをあなたの blog に貼り付けることだってできちゃいます。

技術的な補足をすると...

なお、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);";
キャッシュ可能版

毎回サーバーに取りに行くのは負荷が高いので、実際にはキャッシュ機能をつけています。

キャッシュ可能版のソースを見る

JavaScript

プロフィール

写真

にとよん (nitoyon)

京都の某ベンチャー会社勤務。プログラマ、たまに趣味でデザイン。