JavaScriptのDOMパーザー

XMLHttpRequestのresponseXMLを使う場合、ブラウザによってはcontent-typeによってパーズしないでnullになる場合がある。これは、responseTextで得られるXMLテキストから、DOMパーザーに通せば強制的にDOM Documentにできます。IEでのローカルファイルでresponseXMLがnullになる問題(ローカルファイルでチェックする場合に便利程度ですが)はこれでも解決できるはず。

function parseXml(xmlText) {
  if (window.ActiveXObject) {
    var domDoc = new ActiveXObject('Microsoft.XMLDOM');
    domDoc.async = false;
    domDoc.loadXML(xmlText);
    return domDoc;
  } else if(window.DOMParser) {
    var domParser = new DOMParser();
    return domParser.parseFromString(xmlText, "application/xml");
  } else {
    return null;
  }
}

function createXmlHttp() {
  if (window.ActiveXObject) {
    return new ActiveXObject("Microsoft.XMLHTTP");
  } else if(window.XMLHttpRequest) {
    return new XMLHttpRequest();
  } else {
    return null;
  }
}

function createXmlHttpWithCallback(callback) {
  var xmlhttp = createXmlHttp();
  var handler = function () {
    if (xmlhttp.readyState == 4) {
      if (xmlhttp.status == 200) {
        callback(xmlhttp);
      }
    }
  };
  handler.xmlhttp = xmlhttp;
  handler.callback = callback;
  xmlhttp.onreadystatechange = handler;
  return xmlhttp;
}

function xmlGet(uri, callback) {
  var xmlhttp = createXmlHttpWithCallback(function (xmlhttp) {
    var dom = parseXml(xmlhttp.responseText);
    if (dom != null && dom.documentElement != null) {
      callback(dom);
    }
  });
  xmlhttp.open("GET", uri, true);
  xmlhttp.send(null);
}

使い方は

xmlGet(uri, function (domdoc) {
 domdoc.getElementById(...;
});

IEはActiveXObjectでMocrosoft.XMLDOM(もしくはMSXML2.DOMDocument)を、firefoxOperaはwindow.DOMParserを使えます。

以下チェック用JavaScript:

<?xml version="1.0"?>
<html>
  <head>
    <script language="JavaScript">
      //<![CDATA[
        function load() {
          if (window.ActiveXObject) {
            dump("window.ActiveXObject: exists");
          }
          if (window.XMLHttpRequest) {
            dump("window.XMLHttpRequest: exists");
          }
          if (window.DOMParser) {
            dump("window.DOMParser: exists");
          }
        }
        
        function dump(obj) {
          document.getElementById("dump").innerHTML += obj + "<br />";
        }
      //]]>
    </script>
  </head>
  <body onload="load();">
    <div id="dump"/>
  </body>
</html>

JavaScriptのコーディングスタイル例

HTML上のある要素に対してなにか操作させるJavaScriptをつくる場合、リソースに関することはJavaScriptではなく、なるべくHTML中に宣言的に埋め込むという手がある。

たとえばこんな感じ:

<?xml version="1.0"?>
<html>
  <head>
    <script language="JavaScript">
//<![CDATA[
  function clickToChange(id) {
    var imgSpan = document.getElementById(id);
    var img = imgSpan.getElementsByTagName("img")[0];
    img.index = 0;
    img.onclick = function () {
      var seqs = imgSpan.getElementsByTagName("seq");
      img.index = (img.index + 1) % seqs.length;
      img.src = seqs[img.index].getAttribute("src");
    };
  }
//]]>
    </script>
  </head>
  <body onload="clickToChange('img1');">
    <div>
      いろいろな
      <span id="img1">
        <img src="d00.png" />
        <seq src="d00.png" />
        <seq src="d01.png" />
        <seq src="d02.png" />
      </span>
      文書
    </div>
  </body>
</html>

もしくは:

<?xml version="1.0"?>
<html>
  <head>
    <script language="JavaScript">
//<![CDATA[
  function clickToChange(id) {
    var img = document.getElementById(id);
    img.index = 0;
    img.count = parseInt(img.getAttribute("count"));
    img.onclick = function () {
      img.index = (img.index + 1) % img.count;
      img.src = img.getAttribute("seq" + img.index);
    };
  }
//]]>
    </script>
  </head>
  <body onload="clickToChange('img1');">
    <div>
      いろいろ
      <img id="img1" src="d00.png" 
        count="3"
        seq0="d00.png" 
        seq1="d01.png" 
        seq2="d02.png" />
      文書
    </div>
  </body>
</html>

クリックしたら切り替わる簡単なJavaScriptだけど、シーケンス上のリソースは要素や属性にしてHTML上に埋める。

やりたいこととやっていることを分離するので設計的には良いと思う。コードの置き換えもやりやすいはず。問題はHTMLのvalidな構造を越えるものになることだが、最近はmicroformatとか出ているので、XML的にwellformedで「知らない要素や属性を無視する」とHTMLでvalidであれば問題ない気もする。

(なんでパーマリンクスタイルだと整形テキストのXMLコメント部分が消えるんだ?)

Ajaxの長所・短所・実装に関するメモ (暫定版)のメモ

より、

しかし、Ajax分散モデルは、いわゆるWebサービスの分散モデルとは決定的に異なります。
いわゆるWebサービスの分散モデルは、不特定多数のサービスを検索によって集め、それらを組み合わせて応用システムを構築します。

たしかにSOA的なWeb Servicesとは違う。けれど、AJAXといえどもWebの1コンポーネントであるべきで、設計的にはUIとしての機能よりもまずリソースとしての振り分けが先に行われるべきでしょう。その上で個別のリソースに機能をAJAXで付加したり、もしくはまとめたUIをその上にかぶせることになるのでは。

そういう点では、上の文章では無視されてしまったXMLメッセージのURIとその意味の割付こそが設計の肝ではないでしょうか。クライアントステートを保持しないサーバを実現し、かつリッチなUIを実現するために、AJAXを使うというのが手順であるべきだと思います。こういうことができることこそがAJAXの長所だと思うんですが。つまり、AJAXはRESTスタイル上でのアプリ実装のより使いやすい解だと思うんです。

UIの機能(スタイル?)をトップにして分解しているので変になるのではないかとも思う。まずオンラインゲームとの対比は危ういメタファでしょう。実際のオンゲーはコネクション依存した通信を使うことも多いですし、これはXmlHttpとはかけ離れています。ゲームフレームワーク設計で多いフレーム処理もAJAXのイベントコールバックとは少し離れてしまう。あと、自動セーブという文脈も危うい。リソースへの編集かどうかという設計視点が無ければ、検索エンジンのフォームでもサーバへ自動セーブをさせようとするかもしれませんし、そうなればサーバ処理は一気に複雑になりますから。こういう視点になれば、AJAXは短所だらけになるし、企業AppletやWeb Servicesでの悲劇を繰り返すと思う。これが一般での見解であるとも思えないけど、そういう見方をする人が従来的な企業システム分野にはあるていど居るだろうことは予想がつきます。

ところでより完全な分散システムにするには、XmlHttpRequestでつなげる先は増やしたいところだけど、昨今のプライバシー事情からは、起源サーバは同一であるべきなのでしょう(埋め込み画像でさえそうなのだから)。フロントエンドプロクシになるサーバを挟めばよいわけですけど、こういうソリューションはなんか納得はいかない。このへんもインタラクティブな解がないものだろうか。

AJAXでのXML

つづきに「XMLAjaxの関係は希薄か濃厚か?」では面白い視点がありました。

つまり、私はXMLHttpRequestオブジェクトにおいて、XML文書を扱うことが当然の前提であるという態度を取り、最初のテストプログラムで何のためらいも疑問もなくXML文書を扱うように組み、そしてトラブルにはまっています。

 しかし、大村さん(と彼と同じメーリングリストの仲間)は、まずテキストファイルによる事例で試し、それが成功した時点で調査が完了してしまっています。

 このような点から考えると、世間一般の目から見たAjaxにおけるXMLの存在感は、おそらく希薄であろうと思います。

実際は、リクエストのURIが意味を持っている。そのため、そのURIから得られるXML上の要素セットの意味はURIの意味に従属するものであるため、プレーンテキストで扱ったとしてもXMLで扱ったとしてもふつうは表現上の違いでしかない。つまり大抵はURIを指している時点で、その先の使い方を知ってるはずで、あとは解析コード記述の違いでしかない。

こうなるとAJAXでのXMLは機能としてのメリットが重要になる。たとえば解析コードの扱いやすさ、変異への耐性の高さのためのものになるはずです。このあたりはアプリケーションの規模や長さとも絡んでくるのでなんともいえない(小さいものならプレーンテキストやCSVのほうがむしろ上になる)。

組織としての境界を越えて使うデータ表現であれば、このようなXMLの重要性は増すのだろうけど、そうなることはあまり考えないかもしれない。XMLを使う場合、データの共通構造を意識(設計)することが先に必要だし、それは結構難しいし、その変更は面倒だし、ということじゃないだろうか。

アドホックに変更できるようなデータなら、XMLよりもJSONでかいてevalほうが楽かもしれない。構造化文書(配列、オブジェクト含めて)はどれもシームレスに扱いえればいいんだろうけど。

Java上のプラグインシステム

基本はClassLoaderでのクラス解決をプラグイン記述に併すようにするだけか。
あとはextension-point(Dirk RiehleのRole Model分析)やリソース解決などで便利になるフレームワークを提供する。