W3C勧告XSLTのバージョンXSLT 2.0の「Grouping / グループ化」
XSL Transformations (XSLT) Version 2.0 / W3C Recommendation 23 January 2007の目次に沿った日本語訳です。
当サイト管理人が2009年04月、意訳したものですが、構文解釈の違いや翻訳の違いが含まれるかもしれません。正式文書はW3C 各種仕様書(英語版)である事を予めご了承ください。
14 グループ化 / Grouping 14.1 カレントグループ 14.2 カレントグループ化キー 14.3 [ xsl:for-each-group ]要素 14.4 グループ化の例 |
このセクションで記述した利便性は、一般的な値を基準としてグループ化する為のシーケンスにあるアイテムを許容する為に設計されます。; 例えば、固有の属性における同じ値を持っている要素、または、同じ名前を持つ要素、または、いくつかの他の式における一般的な値を持つ要素のグループ化を許容します。 グループ化は、重複する値を持つアイテムを識別する事から、 同じ利便性はまた、アイテムのシーケンスにある異なった値の選択をも許容し、それは、重複の削除という事です。
注釈:
重複の単純な削除はまた、
主要な関数ライブラリにある
関数 distinct-values
FOを利用して取得する事もできます。
:[関数と演算子]参照。
追記すると、 これらの利便性は、順次的な位置を基準にグループ化、例えば、隣り合う para
要素のグループを選択する事を許容します。
その利便性はまた、固定サイズのグループ化を行う為の容易な方法、例えば、複数の列にあるデータを配列する際に有益である隣り合う3つのノードのグループを識別する事を提供します。
識別したアイテムのグループごとに、それは、グループにおけるシーケンスコンストラクタの評価を可能とします。 グループ化は、識別される事が可能な異なるアイテムのグループをそのような複数のレベルにネストさせる事ができ、その時、 選択した異なるグループ、実行される事が可能な現在のグループにある異なるアイテムの異なるサブグループ間から(ネストできます)。
それはまた、1つ以上のグループにある関与する為の1つのアイテムにおいて可能とします。
current-group
() as
item()*
[定義:
XPath 式における評価文脈は、シーケンスであるカレントグループと呼ばれるコンポーネントを含みます。
カレントグループは、 xsl:for-each-group
要素の1回の反復にある集合体を処理される関連したアイテムのコレクションです。
]
xsl:for-each-group
命令が評価されている間、カレントグループは、
カラでないようにするでしょう。
他の時点では、それは、カラのシーケンスでしょう。
関数 current-group
は、カレントグループを返します。
その関数が受け取る引数は、ありません。
[ERR XTSE1060]
もし、 current-group
関数が、パターン内で利用される場合には、
それは、静的エラーです。
current-grouping-key
() as
xs:anyAtomicType?
[定義: XPath 式における評価文脈は、原子化された値であるカレントグループ化キーと呼ばれるコンポーネントを含みます。 カレントグループ化キーは、 グループ化キー が、カレントグループにある全てのアイテムによって普通に共有されます。 ]
group-by
を伴う xsl:for-each-group
命令の間、または、 group-adjacent
属性が評価されている間、
カレントグループ化キーは、単独の原子化された値になるでしょう。
他の時点では、それは、カラのシーケンスになるでしょう。
関数 current-grouping-key
は、カレントグループ化キーを返します。
たとえ、グループにある全てのアイテムのグループ化キーが、同じ定義によるものであったとしても、
それらが同一である必要はありません。
例えば、他の1つが xs:decimal
である間、1つは、xs:float
になる場合もあります。
current-grouping-key
関数は、 xs:untypedAtomic
から xs:string
に原子化され、投入された後、グループにある初期アイテムのグループ化キーを返すために定義されます。
その関数が受け取る引数はありません。
[ERR XTSE1070]
もし、 current-grouping-key
関数が、パターンの中で利用される場合には、
それは、静的エラーです。
xsl:for-each-group
要素<!-- カテゴリ:
命令 -->
<xsl:for-each-group
select = expression
group-by? = expression
group-adjacent? = expression
group-starting-with? = pattern
group-ending-with? = pattern
collation? = { uri }>
<!-- Content: (xsl:sort*,
sequence-constructor) -->
</xsl:for-each-group>
指示命令であるこの要素は、シーケンスコンストラクタ内で利用される場合があります。
[定義:
xsl:for-each-group
命令は、グループ化キーの普通の値上、または、パターン上、または、マッチしなければならないグループにあるその初期または最終ノード上のいずれかに基づくアイテムのグループに入力シーケンスにあるアイテムの領域を確保します。
]
そのシーケンスコンストラクタは、これらのグループごとに1回評価される xsl:for-each-group
命令の
コンテンツを形成します。
[定義:
グループ化されるアイテムのシーケンスは、汎化(一般化)として参照され、それは、 select
属性に含んだXPath 式を評価する事によって決められます。
]
[定義: 汎化は、シーケンスとして扱われます;このシーケンスにあるアイテムの命令は、汎化命令として参照されます ]。
グループは決してカラになることはありません。
もし、その汎化がカラである場合には、グループの数字はゼロになるでしょう。
グループへのアイテムの割り当ては、 group-by
、 group-adjacent
、 group-starting-with
、 group-ending-with
属性に依存します。
[ERR XTSE1080] これらの4つの属性は、相互に排他です: もし、これら4つの属性の構成でないものが存在する、または、それらが1つ以上存在する場合には、それは、静的エラーです。
[ERR XTSE1090]
もし、 group-by
属性でも group-adjacent
属性でもないものが記述される場合には、それは、 collation
属性を記述する為のエラーです。
[定義:
もし、 group-by
属性、または、 group-adjacent
属性のいずれかが存在する場合には、グループ化キーは、汎化にあるアイテムごとに算出されます。
シーケンスの中にあるアイテムであるそのグループ化キーは、 group-by
属性、または、 group-adjacent
属性、その時に xs:untypedAtomic
値から xs:string
に投入する原子化された結果の中で含んだ式を評価する事によって含まれます。
]
汎化にあるアイテムにおけるグループ化キーを評価する際には、 group-by
、または、 group-adjacent
属性に含んだ式は、
文脈アイテムとしてのアイテム、文脈位置としての汎化命令にあるその位置、文脈サイズとしての汎化のサイズを伴って評価されます。
結果となるシーケンスは、原子化し、
シーケンスにある原子化された各値は、汎化にあるアイテムにおけるグループ化キーとして動作します。
もし、 group-by
属性が存在する場合には、
汎化にあるアイテムは、複数のグループ化キーを持つ場合があります:
それは、 group-by
式がシーケンスを評価するという事です。
そのアイテムは、異なるグループ化キーである時、複数のグループとして含まれます。(それは、ゼロになる場合もあります)。
もし、 group-adjacent
属性が利用される場合には、汎化にある各アイテムは、確実に1つのグループ化キー値を持たなければいけません。
[ERR XTTE1100]
もし、 group-adjacent
属性を評価するグループ化キーが、カラのシーケンス、または、 1つ以上アイテムを含んでいるシーケンスである場合には、 それは、タイプエラーです。
グループ化キーは、それらの動的タイプに適した eq
演算子における規則を利用して比較されます。
タイプの値 xs:untypedAtomic
は、比較前に xs:string
に投入されます。
eq
演算子を利用して比較されない2つのアイテムは、等しくならないように考慮されます、それは、異なるグループに領域確保されるという事です。
もし、その値が、文字列である、
または、区分されない原子化された値である場合には、もし、 collation
属性がある場合には、
collation
属性の有効な値として記述した照合を利用して値が比較され、
もし、 xsl:for-each-group
要素の基準URIに対して関連する場合には、解決されます。
もし、 collation
属性が存在しない場合には、 既定照合が利用されます。
グループ化の目的においては、その値 NaN
は、それ自身と等しい事を考慮されます。
[ERR XTDE1110]
もし、(基準URIに対して解決後に) xsl:for-each-group
を記述したその照合URIが、手法によって認識されない照合である場合には、
それは、回復されない動的エラーです。
(注記については、[ERR XTDE1035]参照。)
照合に関するより多くの情報については、13.1.3 照合を利用するソート参照。
[ERR XTTE1120]
group-starting-with
、または、 group-ending-with
属性が利用される際には、
もし、 select
式を評価した結果が、ノードではないアイテムを含む場合には、それは、タイプエラーです。
もし、 group-by
属性が存在する場合には、
汎化にあるアイテムは、汎化の中で検査されます。
アイテム J ごとに、 group-by
属性にある式は、ゼロ以上のグループ化キー値のシーケンスを作成する為に評価されます。
これらのグループ化キーのそれぞれ1つずつは、もし、既にグループ化キー値を持っているアイテムを保持する為に作成されたグループがある場合には、
J は、グループに追加されます;
他方で新しいグループは、グループ化キー値とその最初のメンバとなる J を伴うアイテムの為に作成されます。
汎化にあるアイテムは、このようにゼロか1または、それ以上のグループに割り当てられる場合があります。 アイテムは、同じグループに一回以上割り当てられる事は決してないでしょう;もし、同じアイテムにおいて2つ以上のグループ化キーが等しい場合には、その重複は、無視されます。 アイテム は、現在、汎化にある固有の位置にあるアイテムを意味し、もし、その汎化が、シーケンスの中でいくつかの異なる位置に同じノードを含む場合には、 グループは、実際は重複するノードを含む場合があります。
グループ番号は、汎化に存在する異なるグループ化キー値の番号と同じになるでしょう。
もし、 group-adjacent
属性が存在する場合には、
汎化にあるアイテムは、汎化の中で検査されます。
もし、アイテムが、(汎化命令にある)汎化の中で先行するアイテムとしての
グループ化キーにおいて同じ値を持つ場合には、それは、その先行するアイテムとしての同じグループに割り当てられます;
他方では、新しいグループが作成され、そしてそのアイテムは、その最初のメンバとなります。
もし、 group-starting-with
属性が存在する場合には、その値は、パターンにしなければいけません。
このケースでは、汎化にあるアイテムは、すべてノードにしなければいけません。
汎化にあるノードは、汎化命令の中で検査されます。 もし、ノードが、パターンとマッチする、または、汎化の中で最初のノードである場合には、新しいグループが作成され、そしてそのノードは、その最初のメンバとなります。 他方で、そのノードは、汎化にある先行するそのノードとして同じグループに割り当てられます。
もし、 group-ending-with
属性が存在する場合には、その値は、パターンにしなければいけません。
このケースでは、汎化にあるアイテムは、すべてノードにしなければいけません。
汎化にあるノードは、汎化命令の中で検査されます。 もし、ノードが、汎化にある最初のノードである、または、もし、汎化の中でその先行するノードがパターンとマッチする場合には、 新しいグループが作成され、そしてそのノードは、その最初のメンバとなります。 他方で、そのノードは、汎化にある先行するそのノードとして同じグループに割り当てられます。
[定義: 各グループにおいては、グループにあるそのアイテムは、グループの初期アイテムとして認知される汎化命令の中で最初のものです。 ]
[定義:
グループ間で最初の出現の命令として参照される命令があります。
グループ G が、仮に、汎化命令の中で G の初期アイテムが、 H の初期アイテムに先行する場合の命令にある場合には、グループ H に優先する為に定義されます。
最初の出現が、仮に、汎化命令の中で G の初期アイテムが、 H の初期アイテムに先行するものとします。
もし、2つのグループ G と H が、(アイテムが両方のグループの中にある為)同じ初期アイテムを持つ場合には、
もし、この初期アイテムの group-by
式を評価する事から得た結果であるシーケンスにある G のグループ化キーが、 H のグループ化キーに先行する場合には、
G は、 H に先行します。
]
[定義:
グループ間で処理命令として参照されるその他の命令があります。
もし、処理命令中のグループ R が、グループ S に先行する場合には、
xsl:for-each-group
命令によって返した結果シーケンスの中では、グループ R を処理する事によって生成したアイテムは、
グループ S を処理する事によって生成したアイテムに先行するでしょう。
]
もし、グループの処理命令を構成する
xsl:for-each-group
要素の中ですぐに続く存在しない xsl:sort
要素がある場合には、
最初の出現の命令です。
他方で、 xsl:sort
要素は、
グループの処理命令を定義する xsl:for-each-group
要素の中ですぐに続きます。
(参照先: 13 ソートする).
それらは、各グループ内にあるアイテムの命令には作用しません。
複数のソートキーコンポーネントは、許容され、そして、第一命令から第二命令まで評価されます。
もし、2つのグループが、それらのソートキーコンポーネントにおいて同じ値を持つ場合には、それらは、最初の出現の命令の中で処理されます。
xsl:sort
要素の select
式は、グループごとに1回評価されます。
この評価中、文脈アイテムは、グループの初期アイテムであり、
文脈位置は、(汎化にある各グループにおけるアイテムの1つである)
汎化命令にある初期アイテムのセットにあるこのアイテムの位置であり、
文脈サイズは、グループの番号であり、カレントグループは、
決定されているソートキー値であるグループであり、そしてまた、カレントグループ化キーは、そのグループにおけるグループ化キーです。
もし、 xsl:for-each-group
命令が、 group-starting-with
、または、 group-ending-with
属性を利用する場合には、
カレントグループ化キーは、カラのシーケンスです。
例えば、この意味は、グループ化キーは、 @category
で、
あなたが、 <xsl:sort select="current-grouping-key()"/>
と書く事によってそれらのグループ化キーの命令の中でグループをソートする事が可能である;、
または、あなたが、 <xsl:sort select="count(current-group())"/>
を書く事によってサイズの命令の中にあるグループをソートする事が可能であるという事です。
xsl:for-each-group
要素に含んだシーケンスコンストラクタは、
処理命令の中にあるグループごとに1回評価されます。
結果となるシーケンスは、 xsl:for-each-group
要素の結果を形成する為の処理命令の中で結合されます。
シーケンスコンストラクタ内では、
その文脈アイテムは、関連するグループの初期アイテムであり、
その文脈位置は、グループの処理命令の中に配置される初期アイテムのシーケンス間にあるこのアイテム(各グループごとの1つのアイテム)の位置であり、
その文脈サイズは、グループの番号であり、
そのカレントグループは、(現時点で・現在)処理されているグループであり、
そしてそのカレントグループ化キーは、そのグループのグループ化キーです。
もし、 xsl:for-each-group
命令が、 group-starting-with
、または、 group-ending-with
属性を利用する場合には、
カレントグループ化キーは、カラのシーケンスです。
これは、 position()
を呼ぶシーケンスコンストラクタの中で効果を持ち、
連続する値 1、 2、 ... last()
を取得します。
スタイルシート関数の評価中、 そのカレントグループとカレントグループ化キーは、 カラのシーケンスが設定され、スタイルシート関数の評価の完了においてそれらの前の値を戻します。
xsl:for-each-group
命令の評価の完了において、
そのカレントグループとカレントグループ化キーは、
それらの前の値を戻します。
次に続く例は、一般の値に基づくノードのリストをまとめます。 結果となるグループは、番号付けされますが、ソートされず、そして、総計は、グループごとに計算されます。
ソースXML文書:
<cities> <city name="Milano" country="Italia" pop="5"/> <city name="Paris" country="France" pop="7"/> <city name="Munchen" country="Deutschland" pop="4"/> <city name="Lyon" country="France" pop="2"/> <city name="Venezia" country="Italia" pop="1"/> </cities>
もっと具体的に言うと、その照準は、4列のテーブルを作成する為にあり、異なる国ごとに1行を含んでいます。
4つの列は、1つめは、行の番号を与えられるシーケンス番号を含む為にあり、;2つめは、国名、3つめは、その国にある都市のカンマ区切りしたアルファベットのリスト、
4つめは、その国にある都市における pop
属性の合計です。
要求した出力:
<table> <tr> <th>位置</th> <th>国</th> <th>都市リスト</th> <th>人気</th> </tr> <tr> <td>1</td> <td>Italia</td> <td>Milano, Venezia</td> <td>6</td> </tr> <tr> <td>2</td> <td>France</td> <td>Lyon, Paris</td> <td>9</td> </tr> <tr> <td>3</td> <td>Deutschland</td> <td>Munchen</td> <td>4</td> </tr> </table>
問題解決:
<table xsl:version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <tr> <th>位置</th> <th>国</th> <th>都市リスト</th> <th>人気</th> </tr> <xsl:for-each-group select="cities/city" group-by="@country"> <tr> <td><xsl:value-of select="position()"/></td> <td><xsl:value-of select="@country"/></td> <td> <xsl:value-of select="current-group()/@name" separator="、 "/> </td> <td><xsl:value-of select="sum(current-group()/@pop)"/></td> </tr> </xsl:for-each-group> </table>
時折、それは、各種の要素から成るグループ化キーを利用する事を必要とします: 例えば、目的のソース文書は、前の例の中で利用した1つにとてもよく似ていますが、(以下の)ように同じ国と都市における複数の入力を許容します:
<cities> <city name="Milano" country="Italia" year="1950" pop="5.23"/> <city name="Milano" country="Italia" year="1960" pop="5.29"/> <city name="Padova" country="Italia" year="1950" pop="0.69"/> <city name="Padova" country="Italia" year="1960" pop="0.93"/> <city name="Paris" country="France" year="1951" pop="7.2"/> <city name="Paris" country="France" year="1961" pop="7.6"/> </cities>
今、私たちは、(国、名前)の組み合わせごとに @pop
の平均値を列挙したいとしましょう。
これを操作する為の1つの方法は、そのキーとなる部分を結合する事です、例えば、<xsl:for-each-group select="concat(@country, '/', @name)">
(のように)。
より柔軟な解決策は、その他のものの内部に直接 xsl:for-each-group
要素をネストする事です:
<xsl:for-each-group select="cities/city" group-by="@country"> <xsl:for-each-group select="current-group()" group-by="@name"> <p><xsl:value-of select="@name"/>, <xsl:value-of select="@country"/>: <xsl:value-of select="avg(current-group()/@pop)"/></p> </xsl:for-each-group> </xsl:for-each-group>
2つのアプローチは、厳密には同じではありません。
もし、コードが、 position()
の値と @name
が並んで出力するように変更された場合には、
最初のアプローチ (混合キー(複合キー)を伴う xsl:for-each-group
要素)は、
2つめのアプローチ(2つのネストした xsl:for-each-group
要素)が、それらの番号(1、 2、 1)となるであろう間に、グループ(1, 2, 3)という番号になるでしょう。
次の例は、一般の値の存在によらないグループを識別しますが、文書命令の中で隣り合うものではありません。
グループは、次の h2
要素になるまで全て p
要素で続く h2
要素で構成されます。
ソースXML文書:
<body> <h2>Introduction</h2> <p>XSLT is used to write stylesheets.</p> <p>XQuery is used to query XML databases.</p> <h2>What is a stylesheet?</h2> <p>A stylesheet is an XML document used to definition convert.</p> <p>Stylesheets may be written in XSLT.</p> <p>XSLT 2.0 introduces new grouping constructs.</p> </body>
要求した出力:
<chapter> <section title="Introduction"> <para>XSLT is used to write stylesheets.</para> <para>XQuery is used to query XML databases.</para> </section> <section title="What is a stylesheet?"> <para>A stylesheet is an XML document used to definition convert.</para> <para>Stylesheets may be written in XSLT.</para> <para>XSLT 2.0 introduces new grouping constructs.</para> </section> </chapter>
問題解決:
<xsl:template match="body"> <chapter> <xsl:for-each-group select="*" group-starting-with="h2"> <section title="{self::h2}"> <xsl:for-each select="current-group()[self::p]"> <para><xsl:value-of select="."/></para> </xsl:for-each> </section> </xsl:for-each-group> </chapter> </xsl:template>
title="{.}"
ではなく、 title="{self::h2}"
の利用は、最初の要素が h2
要素ではないケースを操作する為にあります。
次の例は、関連する要素のグループが、どのようにしてグループにある最初ではなく最後の要素によって識別される事が可能なのかを表現します。
現在、属性 continued="yes"
の欠如は、そのグループの終わりを示します。
ソースXML文書:
<doc> <page continued="yes">Some text</page> <page continued="yes">More text</page> <page>Yet more text</page> <page continued="yes">Some words</page> <page continued="yes">More words</page> <page>Yet more words</page> </doc>
要求した出力:
<doc> <pageset> <page>Some text</page> <page>More text</page> <page>Yet more text</page> </pageset> <pageset> <page>Some words</page> <page>More words</page> <page>Yet more words</page> </pageset> </doc>
問題解決:
<xsl:template match="doc"> <doc> <xsl:for-each-group select="*" group-ending-with="page[not(@continued='yes')]"> <pageset> <xsl:for-each select="current-group()"> <page><xsl:value-of select="."/></page> </xsl:for-each> </pageset> </xsl:for-each-group> </doc> </xsl:template>
次の例は、アイテムが、複数のグループに追加される事が可能な方法を示します。 本のタイトルは、そのタイトルなしでマークアップしたインデックスしている語句ごとに1つのグループが追加されるでしょう。
ソースXML文書:
<titles> <title>A Beginner's Guide to <ix>Java</ix></title> <title>Learning <ix>XML</ix></title> <title>Using <ix>XML</ix> with <ix>Java</ix></title> </titles>
要求した出力:
<h2>Java</h2> <p>A Beginner's Guide to Java</p> <p>Using XML with Java</p> <h2>XML</h2> <p>Learning XML</p> <p>Using XML with Java</p>
問題解決:
<xsl:template match="titles"> <xsl:for-each-group select="title" group-by="ix"> <h2><xsl:value-of select="current-grouping-key()"/></h2> <xsl:for-each select="current-group()"> <p><xsl:value-of select="."/></p> </xsl:for-each> </xsl:for-each-group> </xsl:template>
最後の例では、グループ内ノードのメンバーは文書命令の中にある、また、一般の値上にあるノードの隣接について双方が基準にされます。 このケースでは、そのグループ化キーは、boolean 条件が、 true 、または、 false であり、その為、その効果は、グループ化が、 false である最大シーケンスによって続く true である条件において、ノードの最大シーケンスを確立する事等々です。
ソースXML文書:
<p>Do <em>not</em>: <ul> <li>talk,</li> <li>eat or</li> <li>use your mobile telephone</li> </ul> while you are in the cinema.</p>
要求した出力:
<p>Do <em>not</em>:</p> <ul> <li>talk,</li> <li>eat or</li> <li>use your mobile telephone</li> </ul> <p>while you are in the cinema.</p>
問題解決:
p
要素を作成するこの要求は、始めから終わりまで ul
、または、 ol
要素を含まない氏族ノードの最大シーケンスです。
これは、もし、要素が、 ul
、または、 ol
要素で、その他が false である場合には、 true であるグループ化キーを伴う group-adjacent
を利用する事によって行われる事が可能です:
<xsl:template match="p"> <xsl:for-each-group select="node()" group-adjacent="self::ul or self::ol"> <xsl:choose> <xsl:when test="current-grouping-key()"> <xsl:copy-of select="current-group()"/> </xsl:when> <xsl:otherwise> <p> <xsl:copy-of select="current-group()"/> </p> </xsl:otherwise> </xsl:choose> </xsl:for-each-group> </xsl:template>
15. 正規表現 / Regular Expressions >>