レベル: 中級 Mark Pruett (mark.l.pruett@dom.com), System Architect, Dominion
2008年 3月 18日 Ajax スタイルのサーバー呼び出しには、必ずしも XMLHttp リクエストが必要になるとは限りません。この連載の最終回で紹介する天気バッジ・プロジェクトに対する最後の手法では、公開 Web サービス、JSON (JavaScript Object Notation)、そして動的 script タグを使用します。
これまでの 2 回の記事では、再利用可能な天気バッジを作成する 3 つの手法を紹介しました。それぞれの手法では Ajax (Asynchronous JavaScript™ + XML) 技術、具体的には JavaScript の XMLHttpRequest オブジェクトを使用して天気バッジ・ライブラリーを実装し、NWS (National Weather Service) のサーバーから私のサーバーへの NWS XML データの伝搬には、何らかの形の Web プロキシーを使って Ajax の同一ドメインの問題を回避しました。
 |
よく使われる頭字語
- DOM: Document Object Model
- HTML: Hypertext Markup Language
- RSS: Rich Site Summary
- XML: Extensible Markup Language
- XSLT: Extensible Stylesheet Language Transformation
|
|
同一ドメインの問題については連載第 1 回で詳しく説明しましたが、この問題は、XMLHttp リクエストの送信先が元の Web ページを配信したサーバーに限られてしまうというものです。実際には、Ajax アプリケーションの多くはその単一サーバーの外部の領域からのデータを必要とします。そのため、(例えば、Apache ProxyPass 規則を作成するために) Web サーバー構成にアクセスする必要があるという設計決定や、特殊なサーバー・スクリプトを作成しなければならない設計決定に至ってしまいます。
しかしこの記事で説明するように、今までの手法とは別の手法があります。この手法でなら同一ドメインの問題を回避し、前述した必要条件からプログラマーを解放することができます。
手法その 4 に必要なツール
天気バッジ・ライブラリーのこの最後の実装を詳しく解説する前に、この最後の手法を可能にする以下のツールについて説明します。
- Yahoo! Pipes
- JSON
- 動的 script タグ
Yahoo! Pipes
Yahoo! Pipes は、Web でアクセス可能な各種のデータ (RSS フィードなど) を 1 つにまとめるための Web ベースのツールです。Yahoo! Pipes で使用されているグラフィカル・エディター (図 1 を参照) は、Windows® Internet Explorer® や Firefox などの標準 Web ブラウザーで実行されます。
図 1. Yahoo! Pipes のエディター
パイプを作成するには、ツール・パレットからドラッグしたモジュールをキャンバスに配置します。そして、あるモジュールの出力から別のモジュールの入力にパイプを接続することで、モジュール同士を接続します。
完成した Yahoo! Pipe は一意に決まる URL を持ちます。その URL にアクセスすると、Pipe ロジックが Yahoo! の複数のサーバーで実行されます。これらのサーバーはあらゆるデータ・ソースへのアクセスを処理し、データの操作を実行してから結果を出力します。このデータ出力はデフォルトで RSS フォーマットに設定されていますが、JSON をはじめとするその他のデータ・フォーマットを URL に指定することもできます。この後すぐにわかるように、JSON フォーマットでデータを出力できるということが重要になります。
私の天気バッジ・モジュールで使用する NWS データは、Yahoo! Pipes でデータ・ソースの役割を果たすことができます。Yahoo! Pipe URL へのパラメーターとして指定するのは、4 文字の NWS ステーション ID です。
図 1 に示した Pipe は、前回の天気バッジの実装用に私が作成した Pipe です。Yahoo! Pipes では非常に強力なデータ操作を行うことができますが、ここでそのデータ操作を行う実際の目的は 1 つしかありません。それは、NWS データを XML から JSON に変換することです。
JSON
JSON についてはすでに何度か触れていますが、JSON とは何であるか、そして天気バッジ・アプリケーションで JSON を使用する目的をこれから説明します。
JSON (JavaScript Object Notation) は、JavaScript 言語がネイティブに理解するデータ・フォーマットです。JavaScript プログラムで別途解析が必要になる XML とは異なり、JSON は JavaScript コードです。
JSON と XML を比較するため、まずはリスト 1 に記載するバージニア州リッチモンドの場合の簡略化した NWS XML データを見てください。
リスト 1. 元の XML フォームでの NWS データ
<?xml version="1.0" encoding="ISO-8859-1"?>
<current_observation version="1.0"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation=
"http://www.weather.gov/data/current_obs/current_observation.xsd">
<location>Richmond International Airport, VA</location>
<station_id>KRIC</station_id>
<observation_time>
Last Updated on Jan 26, 1:54 pm EST
</observation_time>
<weather>Mostly Cloudy</weather>
<temperature_string>41 F (5 C)</temperature_string>
<relative_humidity>32</relative_humidity>
<wind_string>Calm</wind_string>
<visibility_mi>10.00</visibility_mi>
<icon_url_base>
http://weather.gov/weather/images/fcicons/
</icon_url_base>
<icon_url_name>bkn.jpg</icon_url_name>
</current_observation>
|
リスト 2 は、上記のデータを JSON フォーマットに変換したものです。
リスト 2. JSON フォーマットに変換した NWS データ
{
"count": 1,
"value":
{"title": "NOAA Regional Weather",
"description": "Pipes Output",
"link": "http:\/\/pipes.yahoo.com\/pipes\/pipe.info?_id=CI6HgSh43BGP8nmJouNLYQ",
"callback": "",
"items":
[
{
"location": "Richmond International Airport, VA",
"station_id": "KRIC",
"observation_time": "Last Updated on Jan 26, 1:54 pm EST",
"weather": "Mostly Cloudy",
"temperature_string": "41 F (5 C)",
"relative_humidity": "32",
"wind_string": "Calm",
"visibility_mi": "10.00",
"icon_url_base": "http:\/\/weather.gov\/weather\/images\/fcicons\/",
"icon_url_name": "bkn.jpg",
}
]
}
}
|
JSON は突き詰めると、単なる JavaScript データ構造に過ぎません。そのため上記に記載した JSON を JavaScript 変数に割り当てれば、簡単に参照することができます (リスト 3 を参照)。
リスト 3. JavaScript コードでの JSON データ構造へのアクセス
var weather = {
"count": 1,
"value":
{
"items":
[
{
"location": "Richmond International Airport, VA",
...
}
]
}
};
alert (weather.value.items[0].location);
|
JavaScript コードのこのスニペットは、簡略化した JSON データを JavaScript 変数 weather に割り当てます。location 要素は JavaScript の alert() 関数へのパラメーターとして参照され、この要素の値がアラート・ウィンドウにポップアップ表示されます (図 2 を参照)。
図 2. JSON によって alert() で表示されたロケーション
ほとんどの JavaScript プログラマーは、JSON データ構造を参照するほうが、同等の XML DOM 構造にアクセスするよりも簡単で直観的だと思うはずです。この理由 1 つをとっても、Ajax アプリケーションでこのデータ・フォーマットの人気がますます高くなっていることの説明になります。
もちろん、Ajax 開発者の心を引き付ける JSON による手法はそれだけではありません。JSON は JavaScript のからくりを使ってデータのプロキシーを不要にしてくれるのです。
動的 script タグ
天気バッジ・ライブラリーに使用した以前の 3 つの手法には、共通することが 1 つだけありました。それは、ブラウザーで実行中の JavaScript プラグラムが私のサーバーからデータを (XMLHttp リクエストにより) 要求しなければならないということです。これは XMLHttp リクエストの同一ドメインの制約によるものです。
前の 3 つの手法のうちの 2 つでは Apache ProxyPass 規則を使用してこの問題に対処し、残りの 1 つでは専用のサーバー・サイド・スクリプトによって NWS サーバーからデータを取得しました。
しかし、私のサーバー構成にアクセスできなかったり、あるいはサーバー・サイド・スクリプトを実行できなかったりする場合はどうすればよいのでしょうか。
そんな場合に使えるのが、JavaScript プログラムが別のドメインにあるサーバーのデータにアクセスできるようにするための手法です。この手法は、スクリプト・タグ・ハックと呼ばれることがあります。
この手法の仕組みを理解するには、まず HTML の script タグについて考えてみてください。HTML の script タグは通常、Web ページに JavaScript コード・ライブラリーをインクルードする手段として組み込まれます (リスト 4 を参照)。
リスト 4. HTML の script タグ
<html>
<head>
<title>An Example</title>
<script language="JavaScript" src="some_javascript_code.js"></script>
|
リスト 4 に記載しているのは、Web ページの最初の数行です。最後の行が script タグで、このタグは some_javascript_code.js という JavaScript ソース・ファイルをサーバーからロードするように指示しています。src 属性タグは、スクリプトが配置されている URL を指定します。この例の場合、スクリプトは同じサーバーにありますが、これは必要条件ではありません。JavaScript コードはどのサーバーからでもインクルードすることができます。
これはつまり、単なる JavaScript コードである JSON データであれば、script タグを使ってブラウザーにロードできるということです。しかし script タグはページが最初にロードされる際にロードされる HTML の一部なので、このことが天気バッジ・アプリケーションに大きく貢献するとは思えません。Ajax ソリューションでは、ページがロードされた後でサーバーへのリクエストを実行できるということが必要だからです。
ただし、JavaScript コードでは Web ページの DOM を操作できるため、Web ページ内のデータを追加、変更することは可能です。そこで、JavaScript コードで script タグを動的に作成し、その script タグを DOM に追加してみることにします。そのコードは、リスト 5 のようになります。
リスト 5. script タグの動的作成
var head = document.getElementsByTagName("head").item(0);
var script = document.createElement ("script");
script.src = "some_javascript_code.js";
head.appendChild (script);
|
上記のコードを実行すると、現行ページの head タグの子として新しい script タグが追加されます。これはリスト 4 の HTML のスニペットに相当するコードですが、some_javascript_code.js の JavaScript コードがロードされるのは、初期ページをロードした後であるという点が異なります。
手法その 4: JSON と動的 script タグ
script タグが天気バッジ・ライブラリーにどう役立つのか、まだ疑問に思っている読者もいるかもしれません。そこで、例えば以下のコードが some_javascript_code.js ファイルのすべての内容であると考えてみてください。
この JavaScript ファイルがブラウザーにロードされると同時に、JavaScript インタープリターがファイルに含まれるすべての実行可能コードを実行します。そのため、この例ではすぐにアラート・ウィンドウが開くことになります。
Yahoo! Pipes から生成される JSON データは JavaScript コードなので、前述した手法を使えば、このデータを動的にロードすることができます。しかし、Yahoo! Pipes による JSON データは実行可能な JavaScript コードではなく、単なる JavaScript データ構造です。JSON データをブラウザーにロードすることはできますが、データが到着したことを知るにはどうすればよいのでしょうか。
動的 script タグを作成するまでのある種のタイマーを設定し、例えば 3 秒後に JSON が到着したと見なすという方法を考えるかもしれませんが、この方法はお世辞にも信頼できるものではなく、結局は不要になります。
各 Yahoo! Pipe には一意に決まる URL があります。リスト 6 に、NWS パイプの場合の URL を示します。
リスト 6. NWS Yahoo! Pipe の URL
http://pipes.yahoo.com/pipes/pipe.run?
&_id=CI6HgSh43BGP8nmJouNLYQ
&textinput1=KRIC
&_run=1
&_render=json
&_callback=myfunction
|
上記の URL は、それぞれのパラメーターをわかりやすく示すために複数の行に分割してあります。まず、_id パラメーターはこれを私のパイプとして識別するためのもので、textinput1 は NWS ステーション ID を指定するパラメーターです。_run はこのパイプを実行する必要があることを指定するパラメーターで、_render では Yahoo! Pipes サーバーに JSON フォーマットでデータを出力するように指示しています。
最後のパラメーター、_callback の役割はと言うと、このパラメーターは Yahoo! サーバーに、指定した関数への呼び出しにデータをラップするように指示します。この例で指定している関数は myfunction() です。Yahoo! Pipes がこの JavaScript プログラムに送り返す内容は、以下のようになります。
myfunction ( {"count":1, ... } );
|
簡潔にするために JSON データの大部分を省略しましたが、考え方はわかってもらえるはずです。前の例ではたった 1 つの alert() 呼び出ししか含まれていない小さな JavaScript ファイルをロードしましたが、その場合とまったく同じく、JavaScript インタープリターは上記のコードを実行して myfunction() を呼び出し、この関数に要求されたすべてのデータを渡します。
コールバック関数を使うということは、いつサーバーが JSON データのロードを終了したかを知る必要はないということです。
それでは、このコールバック関数の内容はどのようなものなのでしょうか。この天気バッジ・ライブラリーの例では、手法その 1 で作成した XMLHttpRequest レスポンス関数と同じように天気データのフォーマット設定の処理をします。手法その 1 のコードでは、必要なデータを responseXML DOM オブジェクトから抽出しましたが、今回必要なのは、リスト 7 の jsonHandler メソッドでの場合と同じく JSON データ構造にアクセスすることだけです。リスト 7 には、天気バッジで JSON 手法を使う場合の完全なコードを記載しています。
リスト 7. 天気バッジ・ライブラリーの JSON/Yahoo! Pipes 実装: weather_badge_ypipes_json.js
var yp = new Array();
var idx = 0;
function weather_badge (nws_id, div_name) {
var callback_obj = "yp[" + idx + "]";
var url = "http://pipes.yahoo.com/pipes/pipe.run?"
+ "&_id=CI6HgSh43BGP8nmJouNLYQ"
+ "&textinput1=" + nws_id
+ "&_run=1"
+ "&_render=json";
yp[idx] = new YPipesWeather (url, div_name, callback_obj);
yp[idx].requestJSON ();
idx++;
}
// The YPipesWeather constructor
function YPipesWeather (ypipes_url, div_name, obj_name) {
this.url = ypipes_url + "&_callback=" + obj_name + ".jsonHandler";
this.div_name = div_name;
}
// The requestJSON method: it builds the script tag
// that launches our request to the Yahoo! server.
YPipesWeather.prototype.requestJSON = function () {
// Dynamically create a script tag. This initiates
// a request to the Yahoo! server.
var head = document.getElementsByTagName("head").item(0);
var script = document.createElement ("script");
script.src = this.url;
head.appendChild (script);
}
// The jsonHandler method: this is our callback function.
// It's called when the JSON is returned to our browser
// from Yahoo!.
YPipesWeather.prototype.jsonHandler = function (json) {
var div = document.getElementById (this.div_name);
var weather = json.value.items[0];
var html = "";
html += "<center>\n";
html += "<b>" + weather.location + "</b><br>\n";
html += weather.weather + "<br>";
html += "<img border='0' src='"
+ weather.icon_url_base
+ weather.icon_url_name
+ "'><br>";
html += weather.temperature_string + "<br>";
html += "Wind: " + weather.wind_string + "<br>";
html += "Humidity: " + weather.relative_humidity + "%<br>";
html += "Visibility: " + weather.visibility_mi + " miles<br>";
html += "<br><span style='font-size: 0.8em; font-weight: bold;'>"
+ weather.observation_time + "</span><br>";
html += "</center>\n";
div.innerHTML = html;
}
|
以前のすべての手法と同じく、上記でも weather_badge() 関数を実装しています。今回この関数に渡すのは、4 文字の NWS ステーション ID と HTML DIV タグの名前です。DIV タグが、このバッジをレンダリングする Web ページの領域となります。
リストの残りの部分では、YPipesWeather という名前の JavaScript オブジェクトを定義しています。この実装は 3 つの関数で構成されます。具体的には、コンストラクター、動的に script タグを作成するメソッド (それによって Yahoo! Pipe を実行します)、そしてコールバック関数です。
この結果、JavaScript ライブラリーは XMLHttpRequest オブジェクトを使用しないにも関わらず、前の 3 つの手法と同じように振る舞うことになります。
図 3 に、この最後の手法での天気バッジのデータ・パイプラインを示します。初期ページのロードが完了した後は、私の Web サーバーに別途アクセスする必要はありません。
図 3. JSON と動的 script タグ
リスト 8 は、JSON バージョンの天気バッジ・ライブラリーを使用する HTML Web ページの例です。
リスト 8. Web ページでの JSON 天気バッジ・ライブラリーの使用例
<html>
<head>
<title>JSON Script Tag Example</title>
<link rel="stylesheet" type="text/css" href="weather.css" />
<script language="JavaScript"
src="weather_badge_ypipes_json.js">
</script>
<script>
function init () {
weather_badge ("KRIC", "target1");
}
</script>
</head>
<body onload="init();">
<h3>JSON Script Tag Example</h3>
<div class="wbadge" id="target1">
Loading...
</div>
</body>
</html>
|
4 つの手法の比較
復習すると、Ajax 天気バッジ・ライブラリーを実装する 4 つの手法が行う内容は以下のとおりです。
- NWS サーバーから XML データを読み取ります。
- 読み取った XML データを構文解析して、必要な部分を抽出します。
- 抽出したデータを HTML にフォーマット設定します。
- HTML を Web ページに表示します。
表 1 に、この 4 つの手法の内容を記載します。
表 1. 天気バッジ・ライブラリーの 4 つのバージョン
| 手法 | 内容 |
|---|
| 1: DOM ツリーの探索 | サーバー上の単純な Web プロキシーが NWS サーバーからデータをプルしてブラウザーに送信します。ブラウザー内部では、JavaScript インタープリターが、返されてきた responseXML DOM ツリーの部分を抽出して HTML フォーマット設定を追加し、これをページ内の DIV タグに挿入します。 |
|---|
| 2: サーバーでの XSLT | サーバー・サイドのスクリプトが NWS サーバーからデータをプルし、XSLT を使用して XML を HTML に変換してから、変換後の HTML スニペットをブラウザーに返します。ブラウザーは単にこのスニペットを DIV タグに挿入するだけとなります。 |
|---|
| 3: クライアント・サイドの XSLT | この手法では (手法その 1 とまったく同じ) 単純な Web プロキシーを使用して XML データをブラウザーに返します。手法その 1 と違う点は、クライアント・サイドの XSLT を使用して XML を HTML に変換し、DIV タグに挿入することです。 |
|---|
| 4: JSON と動的 script タグ | 外部サービス (Yahoo! Pipes) が NWS データを XML から JSON に変換します。天気バッジ・ライブラリーは JSON 特有の性質と JavaScript 言語を利用して変換されたデータをブラウザーに返します。つまり、プロキシーは必要ありません。 |
|---|
どの手法が最適かは、この連載で使用した単純なプログラムの場合でさえも簡単に答えを出すことはできません。そもそも、どんな性質を持ったものが最適なソリューションになるのでしょうか。
最後の手法では、プロキシーが要らなくなるように JSON を使用しています。これによって、サーバー構成を変更したり、サーバー・スクリプトを作成したりすることができない環境での実装が容易になりますが、この利点は裏返してみると、サード・パーティーの Web サービスである Yahoo! Pipes に頼らなければならないということでもあります。Yahoo! がそのサービスを変更した場合、このソリューションが成り立たなくなる可能性を考えなければなりません。
では、手法その 2 はどうでしょう。この手法ではサーバー・サイドで XML を HTML に変換しますが、このようにサーバーに、より多くの作業を任せたほうがよいのでしょうか。この質問に対する答えは、(さまざまなブラウザーのバージョンに対して使われる) JavaScript 言語をどれだけ信頼しているかによってある程度決まります。手法その 3 は、処理の大部分をブラウザーに任せられるということを示していますが、制御が利くサーバー・サイドのスクリプトとは違い、ユーザーが実行するブラウザーのタイプはさまざまです。連載第 2 回で説明したように、XSLT 処理などのタスクを扱う方法はブラウザーによって異なります。JavaScript コードのデバッグは、バックエンド・サーバーのスクリプトのデバッグよりも困難です。そのため、JavaScript 言語がすべてのブラウザーで標準になるまでは、JavaScript コードを最小限にするのが賢い作戦だと言えます。
XSLT 全般に関してはどうでしょう。XML から HTML への変換を XSLT によって抽象化することは理にかなっているのでしょうか。その代わりの手段となるのは、JavaScript コードで XML DOM ツリーにアクセスする手法その 1、そしてソフトウェア・サービスを使って XML を JSON に変換してから JSON データ構造にアクセスする手法その 4 です。XSLT を使用すると、XML から HTML への変換を行う場所をサーバーからブラウザーへ、あるいはブラウザーからサーバーへ XSLT コードを変更せずに簡単に切り替えることができます。しかし、この XSLT 抽象化層のコストも考えなければなりません。私は、手法その 1 のほうが手法その 3 より処理の時間がかからないことを請合います。なぜなら、手法その 1 では一握りの構文解析済みの XML データ要素にアクセスするだけですむからです。ブラウザー・サイドの XSLT は、同じ XML データに対して XSLT プロセッサーを実行しなければならないため、重たい処理になることは間違いありません。
天気バッジのような単純な問題でさえも、正しい唯一の答えを出すことはできません。アルゴリズムの効率性と実行速度を客観的に観察することはできますが、現実の世界では結局のところ、主観的な問題となります。つまり、XSLT による抽象化が速度に関する不利を補うかどうか、そもそも速度が問題になるのかどうかということです。さらにブラウザーで実行するほうが相応しい理由は何か、信頼性をどれだけ重視するか、そして簡潔性、保守の容易性などを基に答えを出すことになります。
私ならどの手法を使用するでしょうか
4 つの手法を説明するためにこの天気バッジを紹介しているのでなかったら、私はどの手法を使っていたと思いますか? これが 1 回限りのソリューションであるという確信があるならば、XMLHttpRequest によって返され XML DOM ツリーにアクセスすればいいだけの手法その 1 の変更バージョンを使用するでしょう。これが多くのバッジ・アプリケーション (株価のアプリケーションや一般的な新規フィードを表示するアプリケーションなど) のなかの最初のバッジ・アプリケーションであれば、いずれかの XSLT ソリューションのような、より抽象的な手法を選ぶことになるでしょうし、他の人にも多種多様なサーバーでバッジを使ってもらうことを目的とするならば、JSON/Yahoo! Pipes の手法を使うことになるでしょう。そしてこのような必要条件が重なってしまったらとしたら、振り出しに戻って最初からやり直すことになるはずです。
ダウンロード | 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|
| Sample code for this series | x-xmlajax.zip | 194KB | HTTP |
|---|
参考文献 学ぶために
製品や技術を入手するために
-
IBM トライアル・ソフトウェア: developerWorks から直接ダウンロードできるトライアル・ソフトウェアで、次の開発プロジェクトを構築してください。
議論するために
著者について
記事の評価
|