sum( 条件つき集計 )

今回は、集計処理です。もっとも、単純な集計処理なら XPathのsum()関数一発なので、もう少し複雑に
してみます。使うXMLデータは、前回までと同じく、フルーツの価格表です。

例題は、30人にフルーツが一個ずつ割り当てられるように、買い物をしたら、合計金額がいくらになるかを
集計するという内容にします。
例えば、りんごは、1個入りが 100円なので、30人分購入すると 30 * 100 = 3000円です。
ところが、イチゴは、30個入りが 500円なので、30人分は 1パックでよいので、500円という感じです。

結論

JSTL:X タグの方が簡単です。
理由は、変数の値を書き換えることができるから。

ソースのダウンロード

JSP-XML-2007-10-01.zipをダウンロードしてください。
いつものように、Maven2 でビルドして、Tomcatにデプロイして、http://localhost:8080/JSP-XML/を開きます。

ドロップダウンリストに「sum(条件つき集計)」を追加していますので、これを選んで
「JSTL:Xタグで変換」または「XSLTで変換」のボタンを押してください。

JSTL:Xタグのポイント

ポイントになるソースは、/jsp/jstl/sum.jspです。
以下に、その抜粋を示します。

キーポイントは、<x:set var="total" select="number(0)"/> で変数 total を宣言している事です。
その後、forループの中で、<x:set var="total" select="$total + $thisTotal"/> の記述により
変数 total に、各フルーツ 30個分の金額が 加算されていきます。

最後に、<p>合計金額=<x:out select="$total"/>円</p>で、合計金額として表示して
完了です。普通のプログラム言語と同じように、変数を使って、データ加工ができますね。

XSLのポイント

次に、XSLTでの記述ですが、こちらは、JSTL:X タグのように、total変数に金額を加算していくという
手法は使えないのです。確かに XSLTには、xsl:variable という変数定義がありますが、変数のスコープが
とても厳しいのです。下の例をご覧ください。

JSTL:X タグのように、totalという変数を number(0) で初期化して、各priceを加算していくように
見えますが、実は思った通りには動きません。合計には 0円と表示されます。

これは、最初にvariable宣言した 変数 total と、xsl:for-each の中で宣言している 変数 totalが、
名前は同じだけど、実体は別物だからです。JSTL:X タグの時は、名前が同じなら、同じ実体だったので、
加算が可能だったのですが。。。

XSLTでは、xsl:for-each の中での variable宣言は、1回の xsl:for-each内だけでの使い捨てとなります。
すなわち、1回目のforループと、2回目のforループの間でも、xsl:for-each の中での variable宣言は
別々の実体になっており、全く合計値を取得できません。

どうやって、XSLTで合計値を加算していくかというと、実は手がありません。XSLTでは無理です。
しかし、無理難題にトライするのが、マニアな開発の醍醐味でもあります。
今回は、XSLTで できない内容を javascript で補完することにします。

最初に、仕込みを行います。下の /jsp/xslt/xsl/sum.xslの抜粋をご覧ください。

xsl:for-eachの中に記述がある、
<div style="display:none" price="true"><xsl:value-of select="$packNum*price"/></div>
の箇所がポイントです。HTML的には、CSSのstyle宣言で非表示にした divタグに、そのフルーツ 30個分の金額を
記入しておきます。

また、最後に、合計金額=<span id="totalPrice"></span>円 として、spanタグを用意しておきます。
XSLT変換の段階では、spanタグの値、つまり合計金額は空欄になっています。

では、XSLTでの仕上げの部分です。/jsp/xslt/sum.jsp の冒頭に javascriptを追加しました。

<body>タグの onLoad="setTotalSum()" 指定により、ページが表示されると、自動的に
javascriptの setTotalSum()関数が実行されます。

setTotalSum()関数は、XSLTで仕込みをしておいた divタグの値を全て合計して、spanタグの値に
書き込むという処理をします。結果として、ページ表示直後に、spanタグに合計金額が設定されます。
サンプルソースでは、その瞬間に alert() でダイアログを出して、書き換える瞬間が分かるようにしています。

書換え前
書換え後

まとめ

ちょっと複雑な集計処理は、XSLTが苦手とする分野です。管理人も XSLT 変換で変数に合計値が記憶できないと
初めて知ったときは、かなり唖然としました。普通のプログラミング言語では考えられない言語仕様です。
どうやら XSLT の目的は、XMLデータを定型的に「取り出す」ことであって、「加工する」ことではないみたいです。

ちなみに、管理人は今回のように、XSLTとjavascirptを併用する場合、sortの必要がなければ JSTL:Xタグを使い、
sortの必要があれば、XSLTを使うことが多いです。前回、JSTL:Xタグでも sortが可能と示しましたが、
あの sort 手法を使うよりは、javascriptを書く方が分かりやすいと思います。