[JavaScript]responseXMLではまった
- 2007-09-08
- カテゴリ: Client Side
- タグ: JavaScript Ajax XMLHttpRequest Tips ブラウザ DOM XML PHP
XMLHttpRequestのresponseXMLを使ってJavaScriptでXMLを読み込んでいたときにいろいろとつまづいたので、サンプルコードを書きつつメモ。間違いやもっといい方法などがあればぜひ教えていただきたい。
サンプルのXMLは、ホットペッパー Webサービスの料理名マスタAPI(application/xml)のデータを使わせてもらった。
一部抜粋すると↓のような感じ。
<Results>
<NumberOfResults>64</NumberOfResults>
<APIVersion>1.11</APIVersion>
<Food>
<FoodCD>R001</FoodCD>
<FoodName>和食全般</FoodName>
</Food>
<Food>
<FoodCD>R002</FoodCD>
<FoodName>懐石料理</FoodName>
</Food>
</Result>
1. クロスドメイン通信はできない
これはXHRの基本的な制約。api.hotpepper.jp以外のドメインからapi.hotpepper.jpのデータをXHRで取ってくることはできない。普通はここでつまづくことはないのだが、サーバーサイドとクライアントサイドで連携しつつ、複数のリクエストをあちこちに送信してると時々間違える。しかも、iframeや<script src="...">はクロスドメイン可能だから余計こんがらがって。
これを解決するには、一度自分のサーバーで外部ドメインのデータを取得して、改めて出力しなおす必要がある。PHPであれば↓の2行で可能(ただし、php.iniで allow_url_fopenが有効になっている必要がある)。
<?php
echo file_get_contents('http://api.hotpepper.jp/Food/V110/?key=guest');
セキュリティ上のリスクは高いが。
2. Content-typeをapplication/xmlに指定しなければいけない
これはFirefoxの仕様。Content-typeがtext/htmlのままになっていると、FirefoxでresponseXMLの値を取得したときにnullになる。
/* use prototype.js */
new Ajax.Request('/proxy.php', {
onComplete: function(xhr) {
window.alert(xhr.responseXML); /* Firefoxではnull */
}
});
これを解決するにはサーバーサイドのプロキシスクリプトを以下のように変更する。
<?php
header('Content-type: application/xml');
echo file_get_contents('http://api.hotpepper.jp/Food/V110/?key=guest');
3. DOMのノードモデルを理解していないと要素中の値を取得できない
DOMのことは詳しくはわからないが、DOMではelementノード(要素ノード)とtextノードというのは別物らしい。つまり、
<APIVersion>1.11</APIVersion>
↑のようなXMLがあったとき、APIVersionという名前のelementノードと、その中の'1.11'というtextノードは別物。'1.11'はAPIVersionの子要素に当たる。従って、APIVersionの中の'1.11'という文字列を取得するには↓のように書く。
var node = xml.getElementsByTagName('APIVersion')[0];
window.alert(node.firstChild.nodeValue); /* 1.11 */
↓ではダメ。
window.alert(node.nodeValue); /* null */
elementノードに対して直接nodeValueは使えないが、かわりにtextContentやtextというプロパティが存在する。ただし、ブラウザ依存なので、クロスブラウザするには↓のように書く必要がある。
(function(node) {
try {
return node.textContent;
} catch (e) {
return node.text;
}
})(xml.getElementById('APIVersion')[0]);
4. IEとその他で改行・空白文字の扱いが違う
HTMLの仕様では、行頭や行末にある改行・空白文字は無視されるが、XMLではそれらもtextノードとして扱われることになっている。しかし、IEは行頭行末の空白文字をtextノードとして扱わずに無視する。
従って、↓のようなXMLで、
<Food>
<FoodCD>R001</FoodCD>
<FoodName>和食全般</FoodName>
</Food>
↓のようなJavaScriptコードを書いた場合、
var node = xml.getElementsByTagName('Food')[0].firstChild;
nodeに割り当てられるのは、IEではFoodCDというelementノードだが、Firefoxなどでは改行と空白からなるtextノードである。
5. IEでは、XMLオブジェクトをfor inで走査できない
下のコードはIE6では動作しない。IE7がどうなのかは知らない。
new Ajax.Request('/proxy.php', {
onComplete: function(xhr) {
var xml = xhr.responseXML;
for (var e in xml)
document.body.innerHTML += e + ' = ' + xml[e] + '<br/>';
}
});
「このオブジェクトではサポートされていない操作です」というメッセージとともに止まる。
6. IEではXMLオブジェクトにプロパティを追加できない
下のコードはIEでは動かない。
new Ajax.Request('/proxy.php', {
onComplete: function(xhr) {
var xml = xhr.responseXML;
xml.fetch = function(tagName) {
return this.getElementsByTagName(tagName)[0].firstChild.nodeValue;
};
window.alert('NumberOfResults = ' + xml.fetch('NumberOfResults'));
window.alert('APIVersion = ' + xml.fetch('APIVersion'));
}
});
IEではXMLオブジェクトにフィールドやメソッドを追加することができない。このようなことをしたい場合は、クロージャを使って下のように書く。
new Ajax.Request('/proxy.php', {
onComplete: function(xhr) {
var xml = xhr.responseXML;
var fetch = function(tagName) {
return xml.getElementsByTagName(tagName)[0].firstChild.nodeValue;
};
window.alert('NumberOfResults = ' + fetch('NumberOfResults'));
window.alert('APIVersion = ' + fetch('APIVersion'));
}
});
これならIEでも動く。
IE6のXMLオブジェクトはJavaScriptでのサポートが遅れている。IE7では改善されているのだろうか?
結論
PHPサイドで適当に処理して、JSONを吐き出すスクリプトを組むのが一番楽なのではないかと思った。
トラックバックURL
- http://liosk.blog103.fc2.com/tb.php/34-f915c9d3
3 件のトラックバック
- XMLパース
-
ajax使っていて自分もはまってしまった事。
http://liosk.blog103.fc2.com/blog-entry-34.html
- 2008-09-08
- 発信元: 独身SEの世迷言
- Python CGI で 掲示板みたいなものを作る~Ajax編~
-
今回は、 JavaScript ファイルを追加して、いわゆる "Ajax" に挑戦してみます。
- 2009-06-11
- 発信元: 新適当マイコン電子工作研究所
- Ajax.RequestのresponseXMLでXMLが取得できない場合の対処法
-
世の中はお盆休み中ですが、この間に滞っていたAjaxごにょごにょプロジェクトを再始動。 なんかトライ&エラーのテストプログラムばかり大量量産してます...。 「Prototype」のAjax.Requ...
- 2009-08-15
- 発信元: ID-Blogger
1 件のコメント
-
ぶっちゃけIEをカットすればいい話
あんなのがWindowsに標準でついてるのがそもそもの問題.- 2009-09-04
- by 名無し
- id:rZ5Dsd/2
- 編集