mode属性を使ってマッチする/しないを切替える

やりたいこと

前後の兄弟要素をdivで囲みたい.具体的には,以下の変換前のxmlのh1oldivで囲み,変換後のxmlにしたい.ちなみに,liの中にあるnew要素もspan class="new"に変換する.

<!-- 変換前のxml -->
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <h1>見出し</h1>
  <ol>
    <li><new/>項目1</li>
    <li>項目2</li>
    <li>項目3</li>
  </ol>
</root>
<!-- 変換後のxml -->
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <div> <!-- h1とolをdivで囲む -->
    <h1>見出し</h1>
    <ol>
      <li><span class="new">new</span>項目1</li> <!-- newをspan class="new"に変換-->
      <li>項目2</li>
      <li>項目3</li>
    </ol>
  </div>
</root>

うまくいかない

最初に試したxslが以下の内容.following-sibling軸を使ってh1と直後のoldivの中で出力している.

<!-- 最初に試したxsl -->
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/root/h1">
    <root>
      <div> <!-- divで囲んで -->
        <xsl:copy-of select="."/> <!-- h1と -->
        <xsl:apply-templates select="following-sibling::ol"/> <!-- olを出力 -->
      </div>
    </root>
  </xsl:template>

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

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

  <xsl:template match="new">
    <span class="{name()}"><xsl:copy-of select="name()"/></span>
  </xsl:template>
</xsl:stylesheet>

実行結果は以下の通り.h1oldivで囲むことはできたが,不要なolも出力している.h1の兄弟要素(following-sibling::ol)として出力したolol自身の2回である.h1の兄弟要素のolは出力して,ol自身の出力はしないスタイルシートを作る必要がある.

<!-- 実行結果 -->
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <div>  <!-- h1とolをdivで囲めた -->
    <h1>見出し</h1>
    <ol>
      <li><span class="new">new</span>項目1</li>
      <li>項目2</li>
      <li>項目3</li>
    </ol>
  </div>
</root>
<ol>  <!-- このolはいらない -->
  <li><span class="new">new</span>項目1</li>
  <li>項目2</li>
  <li>項目3</li>
</ol>

mode属性で出力を制御

match属性にfollowing-sibling::olは書けない.マッチするテンプレートを指定するためにmode属性を使用する.つまり,以下のようにする.

  • ol自身は出力のないテンプレートを適応
  • following-sibling::olにはmodeを指定したテンプレートを適応
<!-- modeを指定したxsl -->
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/root/h1">
    <root>
      <div>
        <xsl:copy-of select="."/>
        <xsl:apply-templates select="following-sibling::ol" mode="mode01"/> <!-- mode属性でマッチするテンプレートを指定 -->
      </div>
    </root>
  </xsl:template>

  <xsl:template match="ol" mode="mode01"> <!--following-sibling::olにマッチするテンプレート -->
    <ol>
      <xsl:apply-templates/>
    </ol>
  </xsl:template>

  <xsl:template match="ol"/> <!-- ol自身にマッチするテンプレート.出力なし -->

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

  <xsl:template match="new">
    <span class="{name()}"><xsl:copy-of select="name()"/></span>
  </xsl:template>
</xsl:stylesheet>

ちなみに

new要素をspan class="new"に変換する必要がなく,ol以下の要素をそのまま出力すれば良い場合は,mode属性なしでスタイルシートを作成できる.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/root/h1">
    <root>
      <div>
        <xsl:copy-of select="."/>
        <xsl:copy-of select="following-sibling::ol"/> <!-- そのまま出力 -->
      </div>
    </root>
  </xsl:template>

  <xsl:template match="ol"/> <!-- ol自身にマッチするテンプレート.出力なし -->
</xsl:stylesheet>

実行環境

  • xalan-j 2.7.2
  • openJDK 1.8.0_121
  • FreeBSD 11.0