前回xsl:iterateについて紹介しましたが、最後につけたWordMLの変換の例はxsl:tryを使ったり、saxon:assignを使ったりして、あまりにも「オタク」すぎたのでもう少し簡単な例を載せておきます.
[入力XML]
<?xml version="1.0" encoding="UTF-8" ?>
<data>
<product type="pencil" maker="三菱" price="100" count="5"/>
<product type="pencil" maker="コーリン" price="90" count="10"/>
<product type="pencil" maker="トンボ" price="110" count="20"/>
<product type="pencil" maker="北星" price="105" count="30"/>
<product type="note" maker="コクヨ" price="200" count="10"/>
<product type="note" maker="ツバメ" price="180" count="15"/>
<product type="note" maker="マルマン" price="150" count="20"/>
</data>
<?xml version="1.0" encoding="UTF-8" ?>
<data>
<product type="pencil" maker="三菱" price="100" count="5"/>
<product type="pencil" maker="コーリン" price="90" count="10"/>
<product type="pencil" maker="トンボ" price="110" count="20"/>
<product type="pencil" maker="北星" price="105" count="30"/>
<product type="note" maker="コクヨ" price="200" count="10"/>
<product type="note" maker="ツバメ" price="180" count="15"/>
<product type="note" maker="マルマン" price="150" count="20"/>
</data>
次のスタイルシートは入力の各商品ごとに、金額の計を計算します.また@type毎に小計を出力、最後に全体の計を出力します.
<xsl:template match="data">
<result>
<xsl:iterate select="product">
<xsl:param name="previousType" select="''" as="xs:string"/>
<xsl:param name="previousTypeAmount" select="0.00" as="xs:decimal"/>
<xsl:param name="previousTotalAmount" select="0.00" as="xs:decimal"/>
<xsl:variable name="amount" select="xs:decimal(@price * @count)" as="xs:decimal"/>
<xsl:variable name="currentTotalAmount"
select="$previousTotalAmount + $amount" as="xs:decimal"/>
<xsl:variable name="currentTypeAmount"
select="$previousTypeAmount + $amount" as="xs:decimal"/>
<xsl:variable name="currentType" select="string(@type)" as="xs:string"/>
<xsl:variable name="typeBreak" select="($previousType ne $currentType) and (string($previousType))" as="xs:boolean"/>
<xsl:if test="$typeBreak">
<total>
<xsl:attribute name="type" select="$previousType"/>
<xsl:attribute name="value" select="$previousTotalAmount"/>
</total>
</xsl:if>
<xsl:copy>
<xsl:copy-of select="@type"/>
<xsl:copy-of select="@maker"/>
<xsl:attribute name="total" select="$amount"/>
</xsl:copy>
<xsl:next-iteration>
<xsl:with-param name="previousType" select="$currentType"/>
<xsl:with-param name="previousTypeAmount" select="if ($typeBreak) then $amount else $currentTypeAmount"/>
<xsl:with-param name="previousTotalAmount" select="$currentTotalAmount"/>
</xsl:next-iteration>
<xsl:on-completion>
<total>
<xsl:attribute name="type" select="$previousType"/>
<xsl:attribute name="value" select="$previousTypeAmount"/>
</total>
<total-amount value="{$previousTotalAmount}"/>
</xsl:on-completion>
</xsl:iterate>
</result>
</xsl:template>
<result>
<xsl:iterate select="product">
<xsl:param name="previousType" select="''" as="xs:string"/>
<xsl:param name="previousTypeAmount" select="0.00" as="xs:decimal"/>
<xsl:param name="previousTotalAmount" select="0.00" as="xs:decimal"/>
<xsl:variable name="amount" select="xs:decimal(@price * @count)" as="xs:decimal"/>
<xsl:variable name="currentTotalAmount"
select="$previousTotalAmount + $amount" as="xs:decimal"/>
<xsl:variable name="currentTypeAmount"
select="$previousTypeAmount + $amount" as="xs:decimal"/>
<xsl:variable name="currentType" select="string(@type)" as="xs:string"/>
<xsl:variable name="typeBreak" select="($previousType ne $currentType) and (string($previousType))" as="xs:boolean"/>
<xsl:if test="$typeBreak">
<total>
<xsl:attribute name="type" select="$previousType"/>
<xsl:attribute name="value" select="$previousTotalAmount"/>
</total>
</xsl:if>
<xsl:copy>
<xsl:copy-of select="@type"/>
<xsl:copy-of select="@maker"/>
<xsl:attribute name="total" select="$amount"/>
</xsl:copy>
<xsl:next-iteration>
<xsl:with-param name="previousType" select="$currentType"/>
<xsl:with-param name="previousTypeAmount" select="if ($typeBreak) then $amount else $currentTypeAmount"/>
<xsl:with-param name="previousTotalAmount" select="$currentTotalAmount"/>
</xsl:next-iteration>
<xsl:on-completion>
<total>
<xsl:attribute name="type" select="$previousType"/>
<xsl:attribute name="value" select="$previousTypeAmount"/>
</total>
<total-amount value="{$previousTotalAmount}"/>
</xsl:on-completion>
</xsl:iterate>
</result>
</xsl:template>
xsl:on-completionはちょうど昔のCOBOLのREAD ~ AT ENDみたいなものですね.出力は次のようになります.
[出力XML]
<?xml version="1.0" encoding="UTF-8"?>
<result>
<product type="pencil" maker="三菱" total="500"/>
<product type="pencil" maker="コーリン" total="900"/>
<product type="pencil" maker="トンボ" total="2200"/>
<product type="pencil" maker="北星" total="3150"/>
<total type="pencil" value="6750"/>
<product type="note" maker="コクヨ" total="2000"/>
<product type="note" maker="ツバメ" total="2700"/>
<product type="note" maker="マルマン" total="3000"/>
<total type="note" value="7700"/>
<total-amount value="14450"/>
</result>
<?xml version="1.0" encoding="UTF-8"?>
<result>
<product type="pencil" maker="三菱" total="500"/>
<product type="pencil" maker="コーリン" total="900"/>
<product type="pencil" maker="トンボ" total="2200"/>
<product type="pencil" maker="北星" total="3150"/>
<total type="pencil" value="6750"/>
<product type="note" maker="コクヨ" total="2000"/>
<product type="note" maker="ツバメ" total="2700"/>
<product type="note" maker="マルマン" total="3000"/>
<total type="note" value="7700"/>
<total-amount value="14450"/>
</result>