txqz memo

XSLTのdocument関数で各地のRSSファイルを読み込んで

いま、Windowsのタスク機能を使って、各地からrdfファイルを持ってきてXSLT変換して一覧するシステムを作ろうとしていて、そのテストとして以下のようなのを書いていた。

<?xml version="1.0" encoding="UTF-8"?>
<antennalist>
   <list>http://txqz.net/feed</list>
   <list>http://hoge.example.com/?mode=rss</list>
</antennalist></code></pre></dd>
<dt>test.xsl</dt>
<dd><pre><code class="xsl"><?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns="http://txqz.net/xmlns/tena"
   version="1.0">

<xsl:output
   method="xml"
   version="1.0"
   encoding="UTF-8"
   omit-xml-declaration="no"
   indent="yes"
/>

<xsl:strip-space elements="*" />

<xsl:template match="antennalist">
   <list>
      <xsl:apply-templates />
   </list>
</xsl:template>

<xsl:template match="list">
   <xsl:for-each select="document(.)/rdf:RDF/item">
      <item url="{link}" name="{../channel/title}" title="{title}" date="{dc:date}">
         <xsl:value-of select="description" />
      </item>
   </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

つまり、捕捉対象のRDFファイルをひとまず

<item url="http://hoge.example.com/fuga/poge/" name="ほげほげ" title="ふがふが" date="1999-07-31T23:59:59+09:00">
   ほげふがぱげ。
</item>

みたいな感じで羅列して、次にそれらを更新時間でソートして一覧にすることにした。

で。MSXSLにかけてみたら、いきなり

未宣言の名前空間の接頭語を参照します: 'rdf'。

というエラーが。速攻で xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" を追加。このあと、今度はダブリンコアの名前空間を宣言していないというエラーが出たので、それも足す。

でエラーは出なくなったが肝心の出力が期待はずれ。何も出てこない。だけど、 xsl:for-each の評価式を rdf:RDF//dc:date とか rdf:RDF//node()[@rdf:about] みたいに接頭辞つきの要素にしたら出力された。うげー。RSS としてデフォルトの名前空間に属している要素はどうやって引っ張ってくるんだー、とまずゲンナリ。

で、しょうがないから検索した。見つかったのはXPathと名前空間というリソース。

XPath中に記されたプレフィックスは、元々のXML文書に記されていたものと無関係に、ここで指定されたもののみ有効になります。特別に注意するべきはデフォルト名前空間の扱いです。基本的に、XPathでデフォルト名前空間を使うことは出来ませんXPathではプレフィックスのない要素・属性名は全て「名前空間無し」と見なされます。だからたとえ処理するXML文書内ではデフォルト名前空間としてプリフィックスが設定されていなくても、XPathを使って処理する際には、適当なプリフィックスを関連付ける必要があります。以下の例ではXHTMLの名前空間URIを"xhtml"というプリフィックスに結びつけて処理しています。

ということで、 xmlns:rss="http://purl.org/rss/1.0/" を追加。以下のようになった。

<?xml version="1.0" encoding="UTF-8"?>

<xsl:stylesheet
   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:dc="http://purl.org/dc/elements/1.1/"
   xmlns:rss="http://purl.org/rss/1.0/"
   xmlns="http://txqz.net/xmlns/tena"
   version="1.0">

<xsl:output
   method="xml"
   version="1.0"
   encoding="UTF-8"
   omit-xml-declaration="no"
   indent="yes"
/>

<xsl:strip-space elements="*" />

<xsl:template match="antennalist">
   <list>
      <xsl:apply-templates />
   </list>
</xsl:template>

<xsl:template match="list">
   <xsl:for-each select="document(.)/rdf:RDF/rss:item">
      <item url="{rss:link}" name="{../rss:channel/rss:title}" title="{rss:title}" date="{dc:date}">
         <xsl:value-of select="rss:description" />
      </item>
   </xsl:for-each>
</xsl:template>

</xsl:stylesheet>

これでめでたく半分完成して、ひとつ賢くなった。