前回の残りで、xsl:for-each-groupのグルーピングの属性、group-starting-withとgroup-ending-withを紹介したいと思います.
group-by, group-adjacentとgroup-starting-withとgroup-ending-withには決定的な違いがあります.前者はXPath式によりグルーピングのキーを指定したのに対し、後者はグルーピングの開始と終了の条件をパターンで指定します.では早速例に入りましょう.
入力データは前回とまったく同じものを使用します.これを、製品ID(@id)の先頭一文字の切り替わりでグルーピングしてみます.
<?xml version="1.0" encoding="UTF-8" ?>
<shipping-data>
<shipping product="鉛筆" id="p-001" date="2010-08-25" count="10"/>
<shipping product="鉛筆" id="p-001" date="2010-08-30" count="30"/>
<shipping product="ノート" id="n-020" date="2010-08-30" count="10"/>
<shipping product="ノート" id="n-030" date="2010-09-05" count="15"/>
<shipping product="消しゴム" id="e-005" date="2010-09-05" count="20"/>
<shipping product="ボールペン" id="b-011" date="2010-08-25" count="05"/>
<shipping product="ボールペン" id="b-012" date="2010-08-30" count="20"/>
</shipping-data>
<shipping-data>
<shipping product="鉛筆" id="p-001" date="2010-08-25" count="10"/>
<shipping product="鉛筆" id="p-001" date="2010-08-30" count="30"/>
<shipping product="ノート" id="n-020" date="2010-08-30" count="10"/>
<shipping product="ノート" id="n-030" date="2010-09-05" count="15"/>
<shipping product="消しゴム" id="e-005" date="2010-09-05" count="20"/>
<shipping product="ボールペン" id="b-011" date="2010-08-25" count="05"/>
<shipping product="ボールペン" id="b-012" date="2010-08-30" count="20"/>
</shipping-data>
結果は次のようになります.(見やすいように編集してあります)
<?xml version="1.0" encoding="UTF-8"?>
<shipping-data>
<product-data id="p">
<shipping product="鉛筆" date="2010-08-25" count="10"/>
<shipping product="鉛筆" date="2010-08-30" count="30"/>
</product-data>
<product-data id="n">
<shipping product="ノート" date="2010-08-30" count="10"/>
<shipping product="ノート" date="2010-09-05" count="15"/>
</product-data>
<product-data id="e">
<shipping product="消しゴム" date="2010-09-05" count="20"/>
</product-data>
<product-data id="b">
<shipping product="ボールペン" date="2010-08-25" count="05"/>
<shipping product="ボールペン" date="2010-08-30" count="20"/>
</product-data>
</shipping-data>
<shipping-data>
<product-data id="p">
<shipping product="鉛筆" date="2010-08-25" count="10"/>
<shipping product="鉛筆" date="2010-08-30" count="30"/>
</product-data>
<product-data id="n">
<shipping product="ノート" date="2010-08-30" count="10"/>
<shipping product="ノート" date="2010-09-05" count="15"/>
</product-data>
<product-data id="e">
<shipping product="消しゴム" date="2010-09-05" count="20"/>
</product-data>
<product-data id="b">
<shipping product="ボールペン" date="2010-08-25" count="05"/>
<shipping product="ボールペン" date="2010-08-30" count="20"/>
</product-data>
</shipping-data>
これを実現するためのテンプレートは次のようなものです.
<xsl:template match="shipping-data">
<shipping-data>
<xsl:for-each-group select="shipping" group-starting-with="shipping[substring(@id,1,1)!=substring(preceding-sibling::*[1]/@id,1,1)]">
<xsl:element name="product-data">
<xsl:attribute name="id" select="substring(./@id,1,1)"/>
<xsl:for-each select="current-group()">
<xsl:sort select="@date"/>
<xsl:copy>
<xsl:copy-of select="@*[name()!='id']"/>
</xsl:copy>
</xsl:for-each>
</xsl:element>
</xsl:for-each-group>
</shipping-data>
</xsl:template>
<shipping-data>
<xsl:for-each-group select="shipping" group-starting-with="shipping[substring(@id,1,1)!=substring(preceding-sibling::*[1]/@id,1,1)]">
<xsl:element name="product-data">
<xsl:attribute name="id" select="substring(./@id,1,1)"/>
<xsl:for-each select="current-group()">
<xsl:sort select="@date"/>
<xsl:copy>
<xsl:copy-of select="@*[name()!='id']"/>
</xsl:copy>
</xsl:for-each>
</xsl:element>
</xsl:for-each-group>
</shipping-data>
</xsl:template>
group-starting-withのポイントはグループの開始となるパターンの指定ですので、上記のように、今回の場合は、"shipping[substring(@id,1,1)!=substring(preceding-sibling::*[1]/@id,1,1)]"となります.
このように、グループの開始を指定するグルーピングでは、グループキーというものが存在しません.group-by, roup-adjacentで使用していたcurrent-grouping-key()の参照は無意味なものとなります.
残りはgroup-starting-withで、グループの終了を指定するものです.次のようなテンプレートに修正してみます.
<xsl:template match="shipping-data">
<shipping-data>
<xsl:for-each-group select="shipping" group-ending-with="shipping[substring(@id,1,1)!=substring(following-sibling::*[1]/@id,1,1)]">
<xsl:element name="product-data">
<xsl:attribute name="id" select="substring(./@id,1,1)"/>
<xsl:for-each select="current-group()">
<xsl:sort select="@date"/>
<xsl:copy>
<xsl:copy-of select="@*[name()!='id']"/>
</xsl:copy>
</xsl:for-each>
</xsl:element>
</xsl:for-each-group>
</shipping-data>
</xsl:template>
<shipping-data>
<xsl:for-each-group select="shipping" group-ending-with="shipping[substring(@id,1,1)!=substring(following-sibling::*[1]/@id,1,1)]">
<xsl:element name="product-data">
<xsl:attribute name="id" select="substring(./@id,1,1)"/>
<xsl:for-each select="current-group()">
<xsl:sort select="@date"/>
<xsl:copy>
<xsl:copy-of select="@*[name()!='id']"/>
</xsl:copy>
</xsl:for-each>
</xsl:element>
</xsl:for-each-group>
</shipping-data>
</xsl:template>
修正点はgroup-starting-withでpreceding-sibling::*[1]を参照していたものをfollowing-sibling::*[1]に変更したのみです.これにより、次のような同じグルーピングの結果を得る事ができます.
<?xml version="1.0" encoding="UTF-8"?>
<shipping-data>
<product-data id="p">
<shipping product="鉛筆" date="2010-08-25" count="10"/>
<shipping product="鉛筆" date="2010-08-30" count="30"/>
</product-data>
<product-data id="n">
<shipping product="ノート" date="2010-08-30" count="10"/>
<shipping product="ノート" date="2010-09-05" count="15"/>
</product-data>
<product-data id="e">
<shipping product="消しゴム" date="2010-09-05" count="20"/>
</product-data>
<product-data id="b">
<shipping product="ボールペン" date="2010-08-25" count="05"/>
<shipping product="ボールペン" date="2010-08-30" count="20"/>
</product-data>
</shipping-data>
<shipping-data>
<product-data id="p">
<shipping product="鉛筆" date="2010-08-25" count="10"/>
<shipping product="鉛筆" date="2010-08-30" count="30"/>
</product-data>
<product-data id="n">
<shipping product="ノート" date="2010-08-30" count="10"/>
<shipping product="ノート" date="2010-09-05" count="15"/>
</product-data>
<product-data id="e">
<shipping product="消しゴム" date="2010-09-05" count="20"/>
</product-data>
<product-data id="b">
<shipping product="ボールペン" date="2010-08-25" count="05"/>
<shipping product="ボールペン" date="2010-08-30" count="20"/>
</product-data>
</shipping-data>