目次


XML 的思索

Firefox 3.0 と XML

人気の Web ブラウザーはリリースごとに XML 開発者に多くのものを提供しますが、新しい 3.0 も例外ではありません

Comments

コンテンツシリーズ

このコンテンツは全#シリーズのパート#です: XML 的思索

このシリーズの続きに乞うご期待。

このコンテンツはシリーズの一部分です:XML 的思索

このシリーズの続きに乞うご期待。

これまで何百もの XML 処理ツールが世の中に登場しましたが、アクションが行われる場所は常に Web ブラウザーであり、XML 開発者にとっては幸いなことに、そのアクションが遅くなることはなかったようです。私は過去数年、開発者が好む Firefox ブラウザーの XML 関連機能についてのシリーズ記事 (「参考文献」を参照) を書いてきましたが、そこでは Firefox 1.5 から 2.0 までを解説しました。Firefox は最近 3.0 にバージョン・アップされ、そこでは全体にわたって数え切れないほどの改善が行われており、XML 処理に関しても素晴らしい新たな進展が大量に見られます。こうした改善の多くは、Web 処理のためのコア・エンジンである Gecko がバージョン 1.8.1 から 1.9 にアップグレードされたことによるものです。

バージョン 3.0 での XML の基本

XML の領域には膨大な技術が積み重ねられていますが、すべては相変わらずパーサーから始まります。Firefox 3 では、基本的な XML 構文解析に 1 つの大きな改善が行われています。これまでの Mozilla ブラウザーでは XML 文書の構文解析は同期型であり、文書が完全にロードされるまで、その文書に対するすべての操作をブロックしていました。それとは対照的に、HTML の構文解析は常に非同期であり、文書の一部の構文解析が終わると、その部分を利用できるようになります。これをユーザーから見ると、ブラウザーがその Web ページの処理を完全に終わる前からそのページの構成が表示されるということです。一方 XML 文書の場合には、その文書が完全に構文解析されるまでユーザーには何も表示されませんでした。これは大規模な XML 文書を処理する際のネックとなり、使いにくさの問題につながっていました。

Firefox 3.0 では、XML のコンテンツ・モデルの作成は、HTML の場合とほとんど同じようにインクリメンタルに行われます。そのため、Web で実際に XML を使う上で大きな違いが生じます。これに関してはいくつかの例外があり、もっとも重要な例外は XSLT の処理がインクリメンタルではないことです。理論的には、制限付きの XPath サブセットを使って XSLT のサブセットをインクリメンタルに適用することができますが、それ自体が大変な作業である上、そうした処理は Firefox 3.0 の想定範囲を超えてしまいます。

Firefox 3.0 に関して私が期待していた改善項目の 1 つが xml:id のサポートです。xml:id をサポートするかどうかに関しては多少の議論がありましたが、パッチは入手可能であり、今後のリリースでサポートされる可能性が十分にあります。一般的な注意事項として、XML 文書で getElementById を使うために Firefox の JavaScript で提供されているのは内部 DTD サブセットのみとなっています (外部サブセットも xml:id も使うことはできません)。どうしても xml:id が必要な場合には、JavaScript で XPath を使って XML 名前空間での属性とローカル名 id に関するクエリーを実行します。

もう 1 つ期待していたけれども、実現されなかった主要な改善項目として、ブラウザーが外部の DTD サブセットをロードするようにユーザーが要求できる機能があります。これに関してもパッチは準備できているようですが、Q&A プロセスを終えるまでの十分な開発リソースが確保できず、Firefox 3.0 のリリースには含められなかったようです。

XSLT に関する大きな改善

Firefox で XSLT を使おうとする人達にとって非常に嬉しいことは、EXSLT がサポートされるようになったことです (EXSLT は XSLT コミュニティーによって開発および認可され、また他の多くの XSLT プロセッサーでサポートされている一連の XSLT 拡張機能です)。Firefox 3.0 では大きなサブセットである EXSLT のサポートを追加しており、node-set 関数を始めとして、XSLT 1.0 による最も厳しい制約に対する有効な回避策が追加されています。EXSLT は複数のモジュールで構成されており、それぞれのモジュールがいくつかの拡張関数と拡張要素を定義しています。Firefox 3.0 では、そのモジュールの中でも選りすぐりのモジュールの選りすぐりの拡張機能を実装しています。それらは次のようなものです。

  • Common: Firefox 3.0 は一連の基本的な汎用関数を実装しています。
    • exsl:node-set を利用すると結果ツリーのフラグメントをノード・セットに変えられるため、それらのノード・セットに XPath を適用することができます。
    • exsl:object-type はストリングやノード・セット、数値、あるいはブール値などオブジェクトの型をレポートするためのイントロスペクション・ツールです。
  • Sets: Firefox 3.0 はノード・セットを扱うために便利な拡張機能をいくつか実装しています。
    • set:difference は、2 つのノード・セット間の違いを抽出し、一方のノード・セットには含まれないがもう一方のノード・セットには含まれるノードからなるノード・セットを返します。
    • set:distinct は、1 つのノード・セットの中で同じストリング値を持つノードが複数ある場合、そのストリング値を持つインスタンスを 1 つだけ残してあとは削除し、同じストリング値を持つノードが複数存在しないようなノード・セットにします。
    • set:intersection は、2 つのノード・セットのどちらにも含まれるノードを抽出し、ノード・セットとして返します。
    • set:has-same-node は、2 つのノード・セットに共通のノードがあるかどうかを判断します (XPath の「=」演算子の場合と同様で、例えば 2 つのノード・セットが単に同じストリング値を持つ異なるノードを共有しているのではなく、実際に同じノードを共有しているかどうかを判断します)。
    • set:leading は、一方のノード・セットに含まれるノードのうち、もう一方のノード・セットの先頭ノードよりも文書内での順序が前に来るノードからなるノード・セットを返します。
    • set:trailing は、一方のノード・セットに含まれるノードのうち、もう一方のノード・セットの先頭ノードよりも文書内での順序が後に来るノードからなるノード・セットを返します。
  • Strings: Firefox 3.0 はストリングを扱うための便利な拡張機能をいくつか実装しています。
    • str:concat は、あるノード・セットの中の各ノードのストリング値を連結したストリングを返します (一定の順番で式を連結する組み込みの concat 関数と比較してみてください)。
    • str:split は、あるパターンを使って、ストリングを一連のサブストリングに分割します (この一連のサブストリングはこの関数の実行時に作成されるノード・セットとして表されます)。
    • str:tokenize は、1 文字のトークン・セットを使って、ストリングを一連のサブストリングに分割します (この一連のサブストリングはこの関数の実行時に作成されるノード・セットとして表されます)。
  • Math: Firefox 3.0 は複数のノード・セットの内容の中から最小あるいは最大の数値を容易に取得できる関数をいくつか実装しています。
    • math:max は指定されたノード・セットの内容の中で最大の数値を返します。
    • math:min は指定されたノード・セットの内容の中で最小の数値を返します。
    • math:highest は、ノードの内容に最大の数値を持つノード・セットを返します。
    • math:lowest は、ノードの内容に最小の数値を持つノード・セットを返します。
  • Regular expressions: Firefox 3.0 では、正規表現の威力を XSLT に適用することができます。
    • regexp:match はストリングに対して正規表現パターンを突き合わせ、一致したサブストリングを含むノード・セットを実行時に作成して返します。
    • regexp:test はストリングが正規表現のパターンと完全に一致するかどうかをチェックします。
    • regexp:replace は正規表現パターンと一致するサブストリングを置き換えます。

EXSLT を使った例

皆さんが作成する独自の変換処理の中で EXSLT を使い始められるように、私は Firefox 3.0 で実装されている関数を多数使用するサンプルを作成しました。ブラウザーでの XSLT の使い方として最適と思われるものは、ある程度構造化されたデータに対してレポートを作成することです。この場合ユーザーに対しては、XSLT 変換を適用するための処理命令を含む XML ファイルを提供します。こうした状況では、クロスブラウザーの互換性をあまり心配しなくてもすむように、必要なブラウザーのバージョンを指定することが多いものです。さらに、かなりの作業負荷をサーバーからユーザーのコンピューターに移します。リスト 1 (employees.xml) は従業員の情報ファイルであり、このファイルに対するレポートを Firefox 3.0 に表示します。

リスト 1. 従業員の情報ファイル (employees.xml)
<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xml" href="employees.xsl"?>
<employees>
  <department id="res">
    <title>Research</title>
    <info>http://example.com/ar-and-dee for more info</info>
    <employee id="111">
      <title>Coordinator</title>
      <name>
        <given>Rene</given>
        <family>Descartes</family>
      </name>
      <location building="PAR1">France</location>
    </employee>
    <employee id="112">
      <title>Project Manager</title>
      <name>
        <given>Abu Ja'far</given>
        <family>Al Kwarizmi</family>
      </name>
      <location building="BAG2">Iraq</location>
    </employee>
  </department>
  <department id="exec">
    <title>Executive</title>
    <info>Home of the head honchos</info>
    <employee id="101">
      <title>Chief Executive Officer</title>
      <name>
        <given>Genghis</given>
        <family>Khan</family>
        <honorific>The Great</honorific>
      </name>
      <location building="MON1">China</location>
    </employee>
  </department>
  <department id="hr">
    <title>Human Resources</title>
    <info>We're happy to serve you at http://example.com/hr</info>
    <employee id="102">
      <title>Manager of Wellness</title>
      <name>
        <given>Ching-Yuen</given>
        <family>Li</family>
      </name>
      <location building="SZE1">China</location>
    </employee>
  </department>
</employees>

先頭にある xml-stylesheet という処理命令に注目してください。この命令はブラウザーに対し、XSLT を使うように指示しています。リスト 2 (employees.xsl) はリスト 1 からレポートを生成するための変換です。

リスト 2. 従業員の情報ファイルからレポートを生成するための変換 (employees.xsl)
<?xml version="1.0" encoding="utf-8"?>
<!-- A -->
<xsl:transform version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:math="http://exslt.org/math"
  xmlns:regex="http://exslt.org/regular-expressions"
  xmlns:set="http://exslt.org/sets"
  xmlns:str="http://exslt.org/strings"
  xmlns="http://www.w3.org/1999/xhtml" 
  exclude-result-prefixes="set math regex str">
  <!-- Notice the namespace declarations for EXSLT.
       Notice also exclude-result-prefixes, since you don't want those
       namespace declarations in the result XHTML
   -->

  <!-- Use XML mode to approximate XHTML output
       (notice the doc type declaration info) -->
  <xsl:output method="xml" encoding="utf-8"
              doctype-public="-//W3C//DTD XHTML 1.1//EN"
              doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"/>

  <xsl:template match="employees">
    <!-- Put the presentation style into a separate file,
         specified using a processing instruction in the output -->
    <xsl:processing-instruction name="xml-stylesheet">
      <xsl:text>type="text/css" href="employees.css"</xsl:text>
    </xsl:processing-instruction>
    <html xml:lang="en">
      <head>
        <title>Employee report</title>
      </head>
      <body>
        <h1>Employee report</h1>
        <table>
          <xsl:apply-templates/>
        </table>
        <hr/>
        <xsl:call-template name="stats"/>
      </body>
    </html>
  </xsl:template>

  <xsl:template name="stats">
    <xsl:variable name="execs"
      select="department[title='Executive']/employee"/>
    <xsl:variable name="employees-in-china"
      select="department/employee[location='China']"/>
    <!-- Use set:has-same-node to check whether the two separate
         XPath queries have any node sets in common -->
    <xsl:if test="set:has-same-node($execs, $employees-in-china)">
      <p>Note: At least one executive presently works in China</p>
    </xsl:if>
    <dl>
      <dt>Countries where employees presently work</dt>
      <dd>
        <!-- Use set:distinct to eliminate duplicate country names
             from the query result -->
        <xsl:for-each select="set:distinct(department/employee/location)">
          <xsl:value-of select="."/>
          <xsl:if test="not(position()=last())">
            <xsl:text>, </xsl:text>
          </xsl:if>
        </xsl:for-each>
      </dd>
      <dt>Newest employee</dt>
      <!-- Use math:highest to determine the highest numerical value of employee ID -->
      <dd><xsl:value-of select="math:highest(department/employee/@id)"/></dd>
    </dl>
  </xsl:template>

  <xsl:template match="department">
    <tr>
      <td colspan="4">
        <!-- Use regular expressions to sniff out URLs from unstructured content -->
        <!-- [1] ensues that if multiple URLs are detected, only the first is used -->
        <a href="{regex:match(info, 'http://[a-zA-Z0-9-_/\.]*', '')[1]}">
          <xsl:value-of select="title"/>
        </a>
      </td>
    </tr>
    <xsl:apply-templates select="employee"/>
  </xsl:template>

  <xsl:template match="employee">
    <tr>
      <td>
        <xsl:value-of select="name/given"/>
        <xsl:text> </xsl:text>
        <xsl:value-of select="name/family"/>
      </td>
      <td>
        <!-- Use str:concat to construct a composite ID from ancestor -->
        <!-- If, for example you added an id attribute to the root element,
             the value would be appended for each employee -->
        <xsl:value-of select="str:concat(ancestor-or-self::*/@id)"/>
      </td>
      <td>
        <xsl:value-of select="title"/>
      </td>
      <td>
        <!-- Standard concat assembles a string from a fixed sequence of expressions -->
        <xsl:value-of select="concat(location, '(', location/@building, ')')"/>
      </td>
    </tr>
  </xsl:template>

</xsl:transform>

このコードでは、EXSLT を使っているところや、その他コメントがあると役立つところに、コメントをたくさん付けてあります。生成される出力は、主にこのパターンを示すために CSS スタイルシートを参照しています。リスト 3 (employees.css) はこの CSS です。

リスト 3. 従業員の情報ファイルからレポートを生成するための表示用スタイルシート (employees.css)
body { background-color: lightblue; }
td { padding-left: 1em; }

リスト 1 を Firefox 3.0 にロードすると、図 1 の内容が表示されます。

図 1. リスト 1 からリスト 3 までを使って生成されたレポートを Firefox 3.0 で表示する
リスト 1 からリスト 3 までを使って生成されたレポートを Firefox 3.0 で表示する
リスト 1 からリスト 3 までを使って生成されたレポートを Firefox 3.0 で表示する

まとめ

Firefox 3.0 には、コアとなる構文解析機能の改善と EXSLT のサポートに加え、名前空間を持つ XML 文書を扱う際に標準に準拠するための修正がいくつか含まれています。DOMAttrModified イベントは名前空間の中の属性を適切に扱えるようになり、また JavaScript の DOM メソッドである getElementsByTagName() は、タグ名の中に名前空間の接頭辞を持つ要素を含むサブツリーに対しても適切に動作するようになりました。Firefox 3.0 では CSS や JavaScript に関して多くの修正が行われているため、XML 開発者の作業が楽になるはずです。

また、XML を利用した最高傑作として非常に人気のあるSVG (Scalable Vector Graphics) のユーザーのために、Firefox 3.0 はさらに便利なものを提供しています。Firefox 3.0 はパターンとマスクをサポートするようになり、その結果リッチな効果を実現するための選択肢が増えました (SVG 1.1 のフィルターはすべてサポートされています)。今やどんな古い Web ブラウザー・オブジェクトにも SVG 変換を適用することができるため、例えば IFRAME を 45 度回転するなど、通常は Canvas の機能を必要とする効果を実現することができます。Mozilla チームは SVG の DOM サポートを実現し、その作業の過程で大量のバグを解決しました。

XML は Web 上で期待されたような成功を収めていないと言う人がいるかもしれません。しかしブラウザーで XML を使って実現できることは既に多数あり、また Web ブラウザーが進化し続けているおかげで、より多くのことが毎年可能になっていきます。Firefox 3.0 は XML 処理に対するコア・パフォーマンスが改善され、また XSLT、DOM、そして SVG に関する機能強化が行われた重要なマイルストーンです。こうした新機能を試すことで何かが悪くなることはありません。クロスブラウザーの要件から、すべての機能を即座に使用することはできないとしても、Web アプリケーションでの最新技術の進化に合わせ、将来に対して備えることはできます。


ダウンロード可能なリソース


関連トピック


コメント

コメントを登録するにはサインインあるいは登録してください。

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=334218
ArticleTitle=XML 的思索: Firefox 3.0 と XML
publish-date=07292008