[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
トラックバック
- 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
コメント
-
ぶっちゃけIEをカットすればいい話
あんなのがWindowsに標準でついてるのがそもそもの問題.- 2009-09-04
- by 名無し
- id:rZ5Dsd/2
- 編集