Jintrick.netagenda2003年01月アーカイブ → 2003年01月19日

dl要素をtable要素に変換(XSLT)

属性ノードの場合、値が正規化されたものがstring-valueになるそうです。というわけで、XSLTにおけるNMTOKENS型属性の扱い (agenda)で書いた複数の属性値を扱うテンプレートに属性値を正規化する手順(normalize-space())は不必要だったのでした。

軸名
意味
主ノード型
ancestor
祖先
要素ノード
attribute
属性
属性ノード
<dl title="XPathにおける軸名とその性質">
<dt>軸名</dt>
<dd>意味</dd>
<dd>主ノード型</dd>

<dt>ancestor</dt>
<dd>祖先</dd>
<dd>要素ノード</dd>

<dt>attribute</dt>
<dd>属性</dd>
<dd>属性ノード</dd>
</dl>

このようなdl要素を次のようなtable要素に変換したいわけです。

XPathにおける軸名とその性質
軸名 意味 主ノード型
ancestor 祖先 要素ノード
attribute 属性 属性ノード
<table summary="XPathにおける軸とその性質">
 <caption>XPathにおける軸名とその性質</caption>
 <thead>
  <tr>
   <th scope="col">軸名</th>
   <th scope="col">意味</th>
   <th scope="col">主ノード型</th>
  </tr>
 </thead>
 <tbody>
  <tr>
   <th scope="row">ancestor</th>
   <td>祖先</td>
   <td>要素ノード</td>
  </tr>
  <tr>
   <th scope="row">attribute</th>
   <td>属性</td>
   <td>属性ノード</td>
  </tr>
 </tbody>
</table>

フラットにだらだらと並んだdt、dd要素をグループ化するテンプレートを書くのは結構面倒なのですが、好い加減私も慣れました。グループ化の応用例はこれで最後にします。

このdl要素を他の普通のdl要素と区別する為名前空間を与えます。その接頭辞をjtrとすれば、jtr:dl要素にマッチするテンプレートを書くことになります。

例によってソース文書となる似非XHTML文書のデフォルトの名前空間がnullであるとしますと:

<xsl:template match="jtr:dl">
 <table summary="{attribute::title}">
  <caption><xsl:value-of select="string(attribute::title)" /></caption>
  <thead>
   <tr>
    <xsl:for-each select="child::dt[position() = 1]">
     <th scope="col"><xsl:value-of select="string(self::*)" /></th>
     <xsl:for-each select="following-sibling::dt[position() = 1]/preceding-sibling::dd">
      <th scope="col"><xsl:value-of select="string(self::*)" /></th>
     </xsl:for-each>
    </xsl:for-each>
   </tr>
  </thead>
   <tbody>
   <xsl:for-each select="child::dt[position() &gt; 1]">
    <xsl:variable name="posDT">
     <xsl:number level="single" count="*" />
    </xsl:variable>
    <tr>
     <th scope="row"><xsl:value-of select="string(self::*)" /></th>
     <xsl:for-each select="following-sibling::dt[position() = 1]/preceding-sibling::dd">
      <xsl:variable name="posDD">
       <xsl:number level="single" count="*" />
      </xsl:variable>
      <xsl:if test="$posDT &lt; $posDD">
       <td><xsl:apply-templates select="child::node()" /></td>
      </xsl:if>
     </xsl:for-each>
    </tr>
   </xsl:for-each>
  </tbody>
 </table>
</xsl:template>

position関数がもう少し賢ければ「まともな」テンプレートが書けそうなものなのですが、取り敢えず見た目シンプルなものを追求すれば私にはこれが限界です。

膨大なデータを扱う場合この方法はナンセンスで、予め変数にdt要素とdd要素の位置をバインドして、for-each要素のselect属性のロケーションパスのみで範囲を特定しなければなりません。そのかわり見た目が腐ってしまい後で見た際に訳が分からなくなってしまいます。この例ではxsl:ifで条件を絞り込むする方法を取っていますが、本来XPathで解決したいところです。


webmaster@jintrick.net
公開: 2003年01月19日
カテゴリ: XSLT