ノードは順不同

はじめに

ノードを入替えるxsltを考えてみよう.例えば,

<first>first</first>
<second>second</second>

でも

<second>second</second>
<first>first</first>

でも変換結果が

<first>first</first>
<second>second</second>

となるようなxsltである.つまり,firstとsecondが順不同にできるxsltを考えてみる.

環境は以下の通り.

  • Microsoft Windows XP SP2+セキュリティパッチたくさん
  • Cygwin 1.5.24(uname -r で確認)
  • Saxon-B 9.0.0.2J

兄弟ノード

サンプルとして以下のようなxmlを考える.

<?xml version="1.0" encoding="utf-8"?>
<root>
  <first>first</first>
  <second>second</second>
  <third>third</third>

  <second>2nd</second>
  <first>1st</first>
  <third>3rd</third>
</root>

必要なのはこれを以下のように出力するxsltである.

<?xml version="1.0" encoding="utf-8"?>
<root>
  <first>first</first>
  <second>second</second>
  <third>third</third>

  <first>1st</first>
  <second>2nd</second>
  <third>3rd</third>
</root>

考え方としては,

  1. まずfirst要素を出力
  2. 前または後にあるsecond要素を出力
  3. thirdを出力

と順番に対象とすれば良い.ということで作ってみた.

<?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"
    encoding="UTF-8"
    indent="yes"/>

  <xsl:template match="/root">
    <xsl:copy>
      <xsl:apply-templates select="first"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="first">
    <xsl:copy-of select="."/>
    <xsl:apply-templates select="following::second[1]|preceding::second[1]"/> <!-- 前方or後方直近のsecond要素にマッチ -->
    <xsl:apply-templates select="following::third[1]"/>
  </xsl:template>

  <xsl:template match="second|third">
    <xsl:copy-of select="."/>
  </xsl:template>
</xsl:stylesheet>

followingまたはprecedingで軸を指定して述語で直近の(1番目の)secondまたはthird要素を指定している.1番目の要素を指定しないと,前方または後方にある全要素が対象になるので注意が必要である.

親子ノード

兄弟だけでなく,親子ノードでも順不同にするxsltを考えてみよう.以下のようなxmlを考える.

<?xml version="1.0" encoding="utf-8"?>
<root>
  <first>
    first
    <second>
      second
      <third>
        third
      </third>
    </second>
  </first>

  <second>
    2nd
    <first>
      1st
      <third>
        3rd
      </third>
    </first>
  </second>
</root>

firstとsecondを入替えて,以下のような結果を出力するxsltを考えよう.

<?xml version="1.0" encoding="utf-8"?>
<root>
  <first>
    first
    <second>
      second
      <third>
        third
      </third>
    </second>
  </first>

  <first>
    1st
    <second>
      2nd
      <third>
        3rd
      </third>
    </second>
  </first>
</root>

考え方としては,

  1. まずfirst要素を出力.rootの子要素であるfirstと孫要素であるfirstがあるので注意.
  2. 親または子にあるsecond要素を出力
  3. thirdを出力.secondの子要素であるthirdと孫要素であるthirdがあるので注意.

と順番に対象とすれば良い.ということで作ってみた.

<?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"
    encoding="UTF-8"
    indent="yes"/>

  <xsl:template match="/root">
    <xsl:copy>
      <xsl:apply-templates select="descendant::first"/> <!-- 子孫のfirst要素が対象 -->
    </xsl:copy>
  </xsl:template>

  <xsl:template match="first">
    <xsl:copy>
      <xsl:value-of select="text()"/>
      <xsl:apply-templates select="child::second|parent::second"/> <!-- 親または子のsecond -->
    </xsl:copy>
  </xsl:template>

  <xsl:template match="second">
    <xsl:copy>
      <xsl:value-of select="text()"/>
      <xsl:apply-templates select="descendant::third"/> <!-- 子孫のthird -->
    </xsl:copy>
  </xsl:template>

  <xsl:template match="third">
    <xsl:copy>
      <xsl:apply-templates select="*|text()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>