XSLTの再帰パターン

みなさんこんにちは!

XSLTでの再帰テンプレートの使用についてお話したいと思います。XSLTを使用する初心者の多くは、アプリケーションを必要とし、そのような問題を解決する方法を知らないタスクに直面しているためです。

一般的な例をいくつか示します。

1.文字列を含むノードがあり、特定の文字(この場合はスペース文字を使用)で部分に分割し、各部分を異なる色で装飾する必要があります。

2.オブジェクトの総数(フォーラムトピックなど)、ページ上のオブジェクトの数、現在のページ番号がわかっているという事実に基づいて、ページ番号(ページャー)を描画します。



まず、再帰パターンを使用していくつかの数値を出力するベース例を示します。



< xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  1. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  2. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  3. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  4. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  5. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  6. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  7. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  8. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  9. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  10. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  11. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  12. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  13. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  14. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  15. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



  16. < xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .



< xsl:template name ="numbers" > < xsl:param name ="current-number" /> < xsl:param name ="max-number" /> < xsl:value-of select ="$current-number" /> <!-- --> < xsl:if test ="$current-number < $max-number" > <!-- , --> < xsl:text > </ xsl:text > <!-- --> < xsl:call-template name ="numbers" > < xsl:with-param name ="current-number" select ="$current-number + 1" /> < xsl:with-param name ="max-number" select ="$max-number" /> </ xsl:call-template > </ xsl:if > </ xsl:template > * This source code was highlighted with Source Code Highlighter .







ここで、xslの条件は、多くの人がすでに推測しているように、再帰を終了することを意図しています。



コードを使用してテンプレートを呼び出す(有効なXMLファイルを入力XMLとして指定できます)





  1. < xsl:テンプレート 一致 = "/" >
  2. < xsl:call-template name = "numbers" >
  3. < xsl:with-param name = "current-number" select = "1" />
  4. < xsl:with-param name = "max-number" select = "50" />
  5. </ xsl:call-template >
  6. </ xsl:テンプレート >
*このソースコードは、 ソースコードハイライターで強調表示されました。


スペースで区切られた1〜50のすべての数値の出力を取得します。



メインアイデアを理解している人は誰でもすぐにそれが適用できる場所と適用すべき場所を理解し、記事の次の部分をスキップできます。



したがって、実際の例。



例1



XMLがあります:





  1. <? xml バージョン = "1.0">
  2. < 文字列 >
  3. < string > bla1 bla2 bla1 bla2 bla1 </ string >
  4. </ 文字列 >
*このソースコードは、 ソースコードハイライターで強調表示されました。


文字列ノード内の行をスペースで分割し、奇数の各要素を緑で、偶数の各要素を赤で表示する必要があります。

XSLT / XPATH 2.0には、文字列を部分に分割できるすばらしいトークナイズ機能があり、それに応じてxsl:for-eachを使用して、各部分で必要な処理を実行できます。 しかし、私が知る限り、XSLT 2.0はSaxonプロセッサーでのみ十分にサポートされています。 組み込みのXSLTブラウザプロセッサとPHPのlibxsltにはないため、再帰的なテンプレートを使用します。





  1. <? xml バージョン = '1.0'>
  2. < xsl:stylesheet version = "1.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" >
  3. <!-一致するだけで文字列、
  4. 次に、ノードのコンテンツがテキストに変換されます。
  5. ただし、文字列内にまだノードがある場合(たとえば、<br />)、
  6. エラーが発生する可能性があります
  7. テキストを使用する場合、
  8. 次に、テキストを一致させる必要があります
  9. コードをわかりやすくするために->
  10. < xsl:template match = "string / text()" >
  11. < xsl:call-template name = "colorer" >
  12. < xsl:with-param name = "text" select = "。" />
  13. <!-他の値はデフォルト値から取得されます->
  14. </ xsl:call-template >
  15. </ xsl:テンプレート >
  16. < xsl:テンプレート = "colorer" >
  17. < xsl:param name = "text" />
  18. <!-デフォルトの区切り文字-スペース->
  19. < xsl:param name = "delimeter" select = "''" />
  20. <!-デフォルトでは、要素の色は奇数です->
  21. < xsl:param name = "even" select = "false" />
  22. < xsl:変数 = "色" >
  23. < xsl:選択 >
  24. < xsl: test = "$ even"の場合 >
  25. < xsl:テキスト ></ xsl:テキスト >
  26. </ xsl:when >
  27. < xsl:その他 >
  28. < xsl:テキスト ></ xsl:テキスト >
  29. </ xsl:それ以外の場合 >
  30. </ xsl: >を 選択します
  31. </ xsl:変数 >
  32. < xsl:選択 >
  33. <!-文字列に区切り文字が含まれる場合->
  34. < xsl:when test = "contains($ text、$ delimeter)" >
  35. <!-次に、セパレータに行を出力します->
  36. < span class = "{$ color}" > < xsl:value-of select = "substring-before($ text、$ delimeter)" /> </ スパン >
  37. <!-そして、残りの行のテンプレートを再度呼び出します->
  38. < xsl:call-template name = "colorer" >
  39. < xsl:with-param name = "delimeter" select = "$ delimeter" />
  40. < xsl:with-param name = "even" select = "not($ even)" />
  41. < xsl:with-param name = "text" select = "substring-after($ text、$ delimeter)" />
  42. </ xsl:call-template >
  43. </ xsl:when >
  44. < xsl:その他 >
  45. < span class = "{$ color}" > < xsl:value-of select = "$ text" /> </ span >
  46. </ xsl:それ以外の場合 >
  47. </ xsl: >を 選択します
  48. </ xsl:テンプレート >
  49. </ xsl:スタイルシート >
*このソースコードは、 ソースコードハイライターで強調表示されました。


この例は手に負えないので、誰かがそのような実装を必要とすることはまずありませんが、非常によく似たタスクに遭遇することがよくあるので、私はそのような例だけでそれを示すことにしました。



例2



これはすでに誰かが仕事で必要とするかもしれない実際の実例です。小さな変更を加えると、誰もがプロジェクトでそれを使用できるようになると思います。

ここでは再帰パターンが使用されているだけでなく、再帰パターンに直接関係しないあらゆる種類のかわいさも持ち込まれているため、結果は戦闘条件により近くなるため、かなり大きいことが判明しました。

したがって、次の形式のXMLがあります。





  1. <? xml バージョン = "1.0">
  2. < フォーラム >
  3. < ページ >
  4. < 現在のページ 番号 = "3" />
  5. < ページあたりのトピック = "15" />
  6. < トピック = "150" />
  7. < link href = "page.php?number =" />
  8. </ ページ >
  9. < テーマ >
  10. < テーマ > theme1 </ テーマ >
  11. < テーマ > theme2 </ テーマ >
  12. < テーマ > theme3 </ テーマ >
  13. < テーマ > theme4 </ テーマ >
  14. < テーマ > theme5 </ テーマ >
  15. < テーマ > theme6 </ テーマ >
  16. < テーマ > theme7 </ テーマ >
  17. < テーマ > theme8 </ テーマ >
  18. < テーマ > theme9 </ テーマ >
  19. < テーマ > theme10 </ テーマ >
  20. < テーマ > theme11 </ テーマ >
  21. < テーマ > theme12 </ テーマ >
  22. < テーマ > theme13 </ テーマ >
  23. < テーマ > theme14 </ テーマ >
  24. < テーマ > theme15 </ テーマ >
  25. </ テーマ >
  26. </ フォーラム >
*このソースコードは、 ソースコードハイライターで強調表示されました。


ページ番号(ポケットベル)を表示する必要があります。 ページ番号の前後に、最初または最後のページにない場合は、前のページと次のページへの遷移を表示する必要があります。 また、ページャーが非常に大きくなることがあるため、一度にすべてではなく、次の数ページに移動するためにリンクのみを表示する必要があるという条件を設定します。

このようなかなり大きなテンプレートを取得します。





  1. <? xml バージョン = "1.0">
  2. < xsl:stylesheet version = "1.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform" >
  3. < xsl:template match = "pages" >
  4. < xsl:call-template name = "page-numbers" >
  5. < xsl:with-param name = "total-results" select = "topics / @ count" />
  6. < xsl:with-param name = "pages-per-page" select = "topics-per-page / @ count" />
  7. < xsl:with-param name = "max-from-current-page" select = "3" />
  8. < xsl:with-param name = "current-page" select = "current-page / @ number" />
  9. < xsl:with-param name = "href" select = "link / @ href" />
  10. </ xsl:call-template >
  11. </ xsl:テンプレート >
  12. < xsl:テンプレート = "ページ番号" >
  13. < xsl:param name = "total-results" />
  14. < xsl:param name = "results-per-page" />
  15. < xsl:param name = "max-from-current-page" />
  16. < xsl:param name = "current-page" />
  17. < xsl:param name = "href" />
  18. <!-総ページ数->
  19. < xsl:変数 = "max-page" select = "ceiling($ total-results div $ $ results-per-per)" />
  20. <!-複数のページがある場合、ページ番号を印刷します->
  21. < xsl: test = "1 <$ max-page"の場合 >
  22. <!-どのページからページ番号の表示を開始するか->
  23. < xsl:変数 = "from-page" >
  24. < xsl:選択 >
  25. <!-現在のページ番号が最大距離よりも大きい場合->
  26. < xsl: test = "$ current-page> $ max-from-current-page"の場合 >
  27. <!-現在のページから指定されたページ数だけ削除された最初のページ->
  28. < xsl:select-of select = "$ current-page-$ max-from-current-page" />
  29. </ xsl:when >
  30. < xsl:その他 > 1 </ xsl:その他 >
  31. </ xsl: >を 選択します
  32. </ xsl:変数 >
  33. <!-ページ番号の出力を終了するページ->
  34. < xsl:変数 = "to-page" >
  35. < xsl:選択 >
  36. <!-現在のページ番号が最後のページ番号よりも最大距離よりも大きい場合->
  37. < xsl: test = "$ max-page-$ current-page> $ max-from-current-page"の場合 >
  38. <!-これは、現在のページから指定したページ数だけ削除された最後のページになります->
  39. < xsl:select-of select = "$ current-page + $ max-from-current-page" />
  40. </ xsl:when >
  41. < xsl:その他 >
  42. < xsl:値の 選択 = "$ max-page" />
  43. </ xsl:それ以外の場合 >
  44. </ xsl: >を 選択します
  45. </ xsl:変数 >
  46. <!-現在のページが最初ではない場合、前のページへのリンクを含む矢印が表示されます->
  47. < xsl: test = "1!= $ current-page" >
  48. <a href = "{$ href} {$ current-page-1}"> <<< / a >
  49. < xsl:テキスト > </ xsl:テキスト >
  50. </ xsl:if >
  51. <!-ページ番号テンプレートを初期値で呼び出します->
  52. < xsl:call-template name = "page-number" >
  53. < xsl:with-param name = "max-page-number" select = "$ to-page" />
  54. < xsl:with-param name = "current-number" select = "$ from-page" />
  55. < xsl:with-param name = "current-page" select = "$ current-page" />
  56. < xsl:with-param name = "href" select = "$ href" />
  57. </ xsl:call-template >
  58. <!-現在のページが最後ではない場合、次のページへのリンクを含む矢印が表示されます->
  59. < xsl:if test = "$ max-page!= $ current-page" >
  60. < xsl:テキスト > </ xsl:テキスト >
  61. <a href = "{$ href} {$ current-page + 1}"> >> </ a >
  62. </ xsl:if >
  63. </ xsl:if >
  64. </ xsl:テンプレート >
  65. < xsl:テンプレート = "ページ番号" >
  66. < xsl:param name = "max-page-number" />
  67. < xsl:param name = "current-number" />
  68. < xsl:param name = "current-page" />
  69. < xsl:param name = "href" />
  70. < xsl:選択 >
  71. <!-現在のページ番号を表示する場合、リンクなし->
  72. < xsl: test = "$ current-number = $ current-page"の場合 >
  73. < xsl:value-of select = "$ current-number" />
  74. </ xsl:when >
  75. <!-リンクのある他のページの数->
  76. < xsl:その他 >
  77. <a href = "{$ href} {$ current-number}">
  78. < xsl:value-of select = "$ current-number" />
  79. </ a >
  80. </ xsl:それ以外の場合 >
  81. </ xsl: >を 選択します
  82. <!-現在の番号が最後ではない場合、次の番号の出力テンプレートを呼び出します->
  83. < xsl:if test = "$ current-number <$ max-page-number" >
  84. < xsl:テキスト > | </ xsl:テキスト >
  85. < xsl:call-template name = "page-number" >
  86. < xsl:with-param name = "max-page-number" select = "$ max-page-number" />
  87. < xsl:with-param name = "current-number" select = "$ current-number + 1" />
  88. < xsl:with-param name = "current-page" select = "$ current-page" />
  89. < xsl:with-param name = "href" select = "$ href" />
  90. </ xsl:call-template >
  91. </ xsl:if >
  92. </ xsl:テンプレート >
  93. < xsl:template match = "@ * | node()" >
  94. < xsl:コピー >
  95. < xsl:apply-templates select = "@ * | node()" />
  96. </ xsl:コピー >
  97. </ xsl:テンプレート >
  98. </ xsl:スタイルシート >
*このソースコードは、 ソースコードハイライターで強調表示されました。


原則として、コード内のコメントによれば、すべてが理解しやすいはずですが、質問があれば、喜んで答えます。



役立つ読書: Alexey Valikov-XSLTテクノロジー -ロシア語聖書XSLT :)



将来的には、XSLTでのキーとモードの使用について書き、また、典型的な小さな初心者の間違いの多くを明らかにしたいと思います。 また、XSLT変換のパフォーマンスについても説明しますが、これは統計計算が多数ある深刻な記事にはなりそうにありません。ほとんどの場合、パフォーマンスのボトルネックを調べるだけです。 これらの記事がどれだけ早く表示されるかについては保証できませんが、特に遅れないようにします。




All Articles