JSONをAJAXと一緒に使う
JSONはJavaScriptであり、AJAXもJavaScriptですから、両者を使って素晴らしいWeb 2.0アプリケーションを構築できないでしょうか。答えはもちろんYESです。今回の記事では、サンプルアプリケーションとしてニュースティッカーを作成しました。図2に、このアプリケーションのアーキテクチャを示します。
この記事の冒頭で、Observerパターンを使ってサンプルアプリケーションを実装すると書きました。図3に、この便利なパターンの簡易バージョンを表すクラス図を示します。

Observerパターン
Observerパターンは、オブジェクト間に1対多の依存関係を与え、あるオブジェクト(Observee)の状態が変化すると、そのすべての従属者(Observer)に通知が送られて、自動的に更新が実行されるプログラミングスタイルです。ほとんどの専門書では、ObserveeはSubjectと呼ばれます。この記事で「Observee」という呼び名を使うのは、このオブジェクトの役割を強調するためです。Observeeはニュースのダウンロードを担当するオブジェクトで、Observerはニュースを受け取るオブジェクトです。
サンプルアプリケーションでは、JavaScriptのMarqueeクラスを使ってObserverを実装し、ContentRetrieverクラスを使ってSubject(以下、Observeeと呼ぶ)を実装します。MarqueeとContentRetrieverが図3の具象クラスを表すことに注意してください。JavaScriptでは抽象クラスが厳密に定義されないので、これらのクラスは抽象クラスを継承しません。抽象クラスを「シミュレート」することもできますが、これは本稿の対象外のテクニックです。Marqueeの役目は、情報をmarquee要素で表示することです。一方、ContentRetrieverの役目は、JSONフォーマットのニュースをAJAX呼び出しでダウンロードすることです。
ContentRetrieverでは、HtmlBuilderヘルパクラスを使って、取得したJSONテキストを解析し、HTMLのmarquee要素で表示するHTMLコードを作成します。このヘルパクラスによって、プレゼンテーションのロジックが分離されます。ニュースのフォーマットを変更することになった場合は、HtmlBuilderのロジックを変更するだけで対応できます。サンプルアプリケーションのフォントや色などの定義にはCSSスタイルシートを使います。フォントや色は好みで変更できます。
以下は、ContentRetrieverクラスのコンストラクタです。
function ContentRetriever(jsonDataRetriever, observerObjectsArray)
{
// JSONデータの取得と解析
this.jsonData = "";
// JSONデータ取得を処理するサーバーサイド
// コンポーネントのURL
this.jsonDataRetriever = jsonDataRetriever;
//AJAXオブジェクト
this.ajaxObj = createHttpRequest();
// observerの配列
this.observerObjectsArray = observerObjectsArray;
}
最初のパラメータは、ニュースの取得を処理するサーバーサイドコンポーネントのURLです。2番目のパラメータは、ContentRetriever(observee)が情報を取得したという通知を受け取るオブジェクト(observer)の配列です。早い話、以上がObserverパターンの基本形です。1人が仕事をこなし、残りの全員が仕事の完了を告げる知らせをただ待っているのです。createHttpRequest関数では、XMLHttpRequestオブジェクトを作成します。このオブジェクトでブラウザの相違点を吸収します。createHttpRequest関数は、この記事のダウンロードサンプルの「ajax.js」ファイルに定義されています。「demo.htm」ファイルは、ティッカーを作成して表示するJavaScriptコードです。このコードは、次のようなものです。
<script type="text/javascript"> var marquee = new Marquee("newsMarquee", 2, true); var contentRetriever = new ContentRetriever( "./tickerDataRetriever.php", [marquee]); //コンテンツを取得する contentRetriever.getContent(); </script>
このコードでは、Marqueeクラス(詳しくは後述)のインスタンスを作成してから、ContentRetrieverオブジェクトを作成し、データ取得を行うPHPスクリプトのURLとobserverオブジェクトの配列(この例ではオブジェクトは1つだけ)を渡します。
以下は、Marqueeコンストラクタのコードです。
function Marquee(elemId, scrollamount, stopOnMouseOver) { this.elemId = elemId; this.scrollamount = scrollamount; this.stopOnMouseOver = stopOnMouseOver; this.dataArray = []; this.errorMessage = "ニュースはありません"; }
Marqueeコンストラクタのパラメータは、以下の3つです。
- elemId
- scrollamount
- stopOnMouseOver
div要素のid。marquee要素のscrollamount属性。スクロール速度を制御できる。 dataArrayプロパティは、取得された解析済みのニュース項目の配列です。errorMessageプロパティは、ニュースのダウンロードに失敗したときに表示するエラーメッセージです。
ニュースを実際に取得するメソッドは、ContentRetrieverクラスのgetContent()です。getContent()メソッドのコードを次に示します。
ContentRetriever.prototype.getContent = function() { if (this.ajaxObj) { var instance = this; this.ajaxObj.open('GET', this.jsonDataRetriever, true); //コールバック関数 this.ajaxObj.onreadystatechange = function(){instance.processDataRetrieved();}; this.ajaxObj.send(null); } };
getContent()では、まずXMLHttpRequestオブジェクトが正常に作成されたことを確認します。次に、HTTPのGETメソッドを使ったAJAX呼び出しと、ニュース取得用のPHPスクリプトのURL、さらに非同期呼び出しを指定するブール値(open()呼び出しの3番目のパラメータtrue)を準備します。非同期の呼び出しなので、呼び出しの完了時に実行されるコールバック関数を定義する必要があります。この例では、processDataRetrievedがコールバック関数です。最後に、他のデータを指定しないで(send()の引数にnullを指定する)AJAX呼び出しを実行します。
実際のデータを取得するサーバーサイドスクリプト(「tickerDataRetriever.php」ファイル)は、とても単純です。
<?php //ニュースのフォルダ $NEWS_FOLDER = "news"; //ニュースのファイル $filename = "news.txt"; //name of the news file $localFile = $NEWS_FOLDER . "/" . $filename; //コンテンツをJSON文字列で返す print(file_get_contents($localFile)); ?>
説明を省くため、今回はJSONフォーマットをテキストファイルにハードコーディングしました。もちろん、実際に使うアプリケーションでは、データベースやRSSフィードなど、動的なコンテンツのソースから情報を取得することになります。今回使うPHPスクリプトでは、JSONテキストを単に取得して解析し、ContentRetrieverに渡します。以下は、「news.txt」の内容です。
{
"items" :
[
{
"title" : "This is the first item's title",
"link" : "http://www.alessandrolacava.com",
"description" : "This is the description of the \"first\" news.
The link takes you to my Web site"
},
{
"title" : "Second item's title",
"link" : "http://www.json.org",
"description" : "You can appreciate how easily you
can parse JSON text. This link takes you to the JSON Web site"
},
{
"title" : "This is the third item's title.",
"link" : "http://www.devx.com",
"description" :
"The link for this item takes you to the DevX Web site"
}
]
}
ご覧の通り、このファイルの内容はオブジェクト配列を表すJSON文字列です。各オブジェクトはニュース項目を表し、title、link、descriptionの3つのプロパティを持ちます。最初の項目のdescription値で二重引用符をエスケープしていることに注意してください(\")。値の中で二重引用符を使うには、エスケープする必要があります。そうしないと、構文の解析時に二重引用符がフィールド値の終端と解釈されます。この記事の最後で、動的コンテンツの取得時にこのエスケープを自動化する方法を説明します。
取得した情報(JSONテキスト)を処理する関数は、次のようなコードです。
ContentRetriever.prototype.processDataRetrieved = function() { //要求が完了したか if (this.ajaxObj.readyState == 4) { //要求は正常に処理されたか if (this.ajaxObj.status==200) { //応答は空かどうか if(this.ajaxObj.responseText != null && this.ajaxObj.responseText.length > 0) { var htmlBuilder = new HtmlBuilder(this.ajaxObj.responseText); //JSONデータを解析する this.jsonData = htmlBuilder.getHtml(); } else { //エラー発生時はサイズ0の配列を返す this.jsonData = []; } // observerに通知する this.notify(); } } };
このコードでは、まず要求が正常に実行されたかどうかを確認します。次に、HtmlBuilderオブジェクトをインスタンス化し、これにJSONテキストを渡します。HtmlBuilderのgetHtmlメソッドでJSON文字列を解析し、marqueeに表示するHTMLコードを作成します。これが終わると、HtmlBuilderからnotifyメソッドを呼び出します。
//すべてのobserverに通知する ContentRetriever.prototype.notify = function() { //observerのコールバック関数を呼び出して、作成した配列を渡す for(var i = 0; i < this.observerObjectsArray.length; i++) { this.observerObjectsArray[i].update(this.jsonData); } };
notifyメソッドで、登録済みのすべてのobserver(この例ではmarqueeただ1つ)のupdateメソッドを呼び出します。
HtmlBuilderのgetHtml関数は、取得したJSONコードからHTMLを作成する変換処理を行います。
//HTMLコードを作成し、項目のJavaScript配列として返す HtmlBuilder.prototype.getHtml = function() { var ret = []; //JSONテキストをデコードする var obj = this.decodeJson(); //配列を取得する var items = obj.items; //配列を使ってHTMLコードを作成する for(var i = 0; i < items.length; i++) { var item = ""; //タイトルを作成する item += this.buildTitle(items[i]); item += "<br />"; //説明文を作成する item += this.buildDescription(items[i]); item += "<br />"; ret.push(item); } return ret; };
この関数では、JSONテキストを解析し、一連のHTML文字列を作成して、これをJavaScript配列としてContentRetrieverに返します。実際のJSONテキスト解析は、decodeJson関数で行います。
HtmlBuilder.prototype.decodeJson = function() { return eval("(" + this.jsonString + ")"); };
ご覧のように、JSONデータ全体を解析するコードがわずか1行で済んでいます。たいしたものです。そうでもないとお思いの方は、試しに同じニュースをXMLで表して、JavaScriptで解析してみるとよいでしょう。目からうろこが落ちること請け合いです。
getHtml関数の残りの部分では、ニュースを使いやすいフォーマットに整え、ニュース項目の配列を作成します。この配列は、ニュースをHTMLのmarquee要素に表示するMarqueeのupdateメソッドに渡されるパラメータです。

