Как я могу разбить строку таблицы в xsl после указанного количества? - PullRequest
3 голосов
/ 18 декабря 2008

У меня есть следующий xsl, который сортирует мой xml по алфавиту:

<xsl:template match="/">
  <xsl:apply-templates />
</xsl:template> 

<xsl:key name="rows-by-title" match="Row" use="translate(substring(@Title,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
<xsl:variable name="StartRow" select="string('&lt;tr &gt;')" />

<xsl:template name="Meunchian" match="/dsQueryResponse/Rows">
  <table>
    <tr>
      <xsl:for-each select="Row[count(. | key('rows-by-title', translate(substring(@Title,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ'))[1]) = 1]">
        <xsl:sort select="translate(substring(@Title,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
        <!-- Puts out the title -->
        <td>
          <xsl:value-of select="translate(substring(@Title,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
        </td>
        <!-- Now all it's children -->
        <xsl:for-each select="key('rows-by-title', translate(substring(@Title,1,1),'abcdefghijklmnopqrstuvwxyz','ABCDEFGHIJKLMNOPQRSTUVWXYZ'))">
          <xsl:value-of select="@Title" /><br/>
        </xsl:for-each>
      </xsl:for-each>
   </tr>
  </table>
</xsl:template>

XML:

<dsQueryResponse>
  <Rows>
    <Row Title="Agenda" />
    <Row Title="Policy" />
    <Row Title="Policy" />
    <Row Title="Report" />
    <Row Title="Report" />
  </Rows>
</dsQueryResponse>

Теперь я хочу разбить строку таблицы через каждые 4 столбца, которые выводятся, чтобы вывод выглядел примерно так:

ABCD
EFGH
IJKL
MNOP
QRST
UVWX
YZ

Кто-нибудь может предложить лучший способ достичь этого?

Большое спасибо

Ответы [ 4 ]

3 голосов
/ 19 декабря 2008

Вот мое решение.

С помощью параметров "per-row" и "show-empty" вы можете решить, хотите ли вы показывать пустые ячейки или хотите скрыть их. Я уверен, что существует гораздо более элегантная версия, но я не смог придумать ее. ;-) Комментарии приветствуются.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" version="4.0" encoding="iso-8859-1" indent="yes"/>

  <xsl:key name="rows-by-title" match="Row" use="translate(substring(@Title, 1, 1), 'abcdefghijklmnopqrstuvwxyz', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
  <xsl:variable name="alphabet" select="string('ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />
  <xsl:variable name="per-row" select="number(4)" /> 
  <xsl:variable name="show-empty" select="false()" />

  <xsl:template match="/">
    <xsl:apply-templates select="dsQueryResponse/Rows" />
  </xsl:template> 

  <xsl:template match="Rows">
    <table>
      <xsl:call-template name="create-rows" />
    </table>
  </xsl:template>

  <xsl:template name="create-rows">
    <xsl:param name="index" select="1" />

    <xsl:variable name="letters">
      <xsl:call-template name="next-letters">
        <xsl:with-param name="index" select="$index" />
      </xsl:call-template>
    </xsl:variable>

    <xsl:if test="$letters != ''">
      <tr title="{$letters}">
        <xsl:call-template name="create-cells">
          <xsl:with-param name="letters" select="$letters" />
        </xsl:call-template>
      </tr>
    </xsl:if>

    <xsl:if test="string-length($letters) = $per-row">
      <xsl:call-template name="create-rows">
        <xsl:with-param name="index" select="string-length(substring-before($alphabet, substring($letters, string-length($letters), 1))) + 2" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <xsl:template name="next-letters">
    <xsl:param name="index" />

    <xsl:variable name="letter" select="substring($alphabet, $index, 1)" />

    <xsl:variable name="letters">
      <xsl:if test="$index &lt;= string-length($alphabet)">
        <xsl:if test="$show-empty or key('rows-by-title', $letter)">
          <xsl:value-of select="$letter" />
        </xsl:if>

        <xsl:call-template name="next-letters">
          <xsl:with-param name="index" select="$index + 1" />
        </xsl:call-template>
      </xsl:if>
    </xsl:variable>

    <xsl:value-of select="substring($letters, 1, $per-row)" />
  </xsl:template>

  <xsl:template name="create-cells">
    <xsl:param name="letters" />

    <xsl:variable name="letter" select="substring($letters, 1, 1)" />

    <xsl:if test="$letter != ''">
      <td title="{$letter}">
        <strong>
          <xsl:value-of select="$letter" />
        </strong>
        <xsl:apply-templates select="key('rows-by-title', $letter)">
          <xsl:sort select="@Title" />
        </xsl:apply-templates>
      </td>
      <xsl:call-template name="create-cells">
        <xsl:with-param name="letters" select="substring($letters, 2, string-length($letters) - 1)" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <xsl:template match="Row">
    <br />
    <xsl:value-of select="@Title" />
  </xsl:template>

</xsl:stylesheet>

С этим входом:

<dsQueryResponse>
  <Rows>
    <Row Title="Agenda" />
    <Row Title="Policy" />
    <Row Title="Policy" />
    <Row Title="Report" />
    <Row Title="Report" />
    <Row Title="Test2" />
    <Row Title="Test1" />
    <Row Title="Boo" />
    <Row Title="Foo" />
  </Rows>
</dsQueryResponse>

Этот вывод получен (атрибуты title были только для отладки. Я оставил их, удалите их в любое время):

<table>
  <tr title="ABFP">
    <td title="A">
      <strong>A</strong>
      <br>Agenda
    </td>
    <td title="B">
      <strong>B</strong>
      <br>Boo
    </td>
    <td title="F">
      <strong>F</strong>
      <br>Foo
    </td>
    <td title="P">
      <strong>P</strong>
      <br>Policy
      <br>Policy
    </td>
  </tr>
  <tr title="RT">
    <td title="R">
      <strong>R</strong>
      <br>Report
      <br>Report
    </td>
    <td title="T">
      <strong>T</strong>
      <br>Test1
      <br>Test2
    </td>
  </tr>
</table>
1 голос
/ 19 декабря 2008

Этот вопрос необходимо отредактировать, чтобы каждый мог понять, в чем на самом деле проблема . Комментарий Томалака показывает, что ОП " хочет списки элементов в алфавитно упорядоченной сетке. Один список для каждой буквы. Четыре буквы по горизонтали, столько же, сколько требуется по вертикали "

Следующее преобразование :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
 extension-element-prefixes="ext"
 >

 <xsl:variable name="vDoc" select="/"/>
 <xsl:variable name="vNumCols" select="4"/>

 <xsl:variable name="vLower"
  select="'abcdefghijklmnopqrstuvwxyz'"
  />

 <xsl:variable name="vUpper"
  select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"
  />

  <xsl:key name="rows-by-FirstLetter" match="Row"
  use="translate(substring(@Title,1,1),
                 'abcdefghijklmnopqrstuvwxyz',
                 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />

  <xsl:variable name="vrtfStartLetters">
    <xsl:for-each select=
    "/*/*/Row
           [count(.
               |
                key('rows-by-FirstLetter',
                   translate(substring(@Title,1,1),
                             $vLower,
                             $vUpper)
                  )[1]
                  )
              = 1
           ]">

         <startLetter>
           <xsl:value-of select=
             "translate(substring(@Title,1,1),
                      $vLower,
                      $vUpper)"/>
         </startLetter>
      </xsl:for-each>
    </xsl:variable>

    <xsl:variable name="vStartLetters" select=
      "ext:node-set($vrtfStartLetters)"/>

  <xsl:template match="Rows">
    <table>
      <xsl:apply-templates select=
       "$vStartLetters/*[position() mod $vNumCols = 1]">
        <xsl:with-param name="pDoc" select="$vDoc"/>
        <xsl:with-param name="pNumCols" select="$vNumCols"/>
      </xsl:apply-templates>
    </table>
  </xsl:template>

  <xsl:template match="startLetter">
    <xsl:param name="pDoc"/>
    <xsl:param name="pNumCols" select="10"/>
    <tr>
      <xsl:apply-templates mode="copy" select=
      ". | following-sibling::*
               [not(position() >= $pNumCols)]">
         <xsl:with-param name="pDoc" select="$pDoc"/>
         <xsl:sort/>

      </xsl:apply-templates>
    </tr>
  </xsl:template>

  <xsl:template match="startLetter" mode="copy">
    <xsl:param name="pDoc"/>

    <xsl:variable name="pThis" select="."/>

    <td>
      <xsl:value-of select="."/>
      <br />
      <table>
     <xsl:for-each select="$pDoc">
      <xsl:for-each select="key('rows-by-FirstLetter', $pThis)">
        <tr><td><xsl:value-of select="@Title"/></td></tr>
      </xsl:for-each>
     </xsl:for-each>
    </table>
    </td>
  </xsl:template>
</xsl:stylesheet>

при применении к этому документу XML :

<dsQueryResponse>
  <Rows>
    <Row Title="Agenda" />
    <Row Title="Accrual" />
    <Row Title="Ads" />
    <Row Title="Averages" />
    <Row Title="Bindings" />
    <Row Title="Budget" />
    <Row Title="Cars" />
    <Row Title="Categories" />
    <Row Title="Costs" />
    <Row Title="Policy" />
    <Row Title="Politics" />
    <Row Title="Reevaluations" />
    <Row Title="Report" />
  </Rows>
</dsQueryResponse>

дает желаемый результат :

<table>
  <tr>
    <td>A
      <br/>
      <table>
        <tr>
          <td>Agenda</td>
        </tr>
        <tr>
          <td>Accrual</td>
        </tr>
        <tr>
          <td>Ads</td>
        </tr>
        <tr>
          <td>Averages</td>
        </tr>
      </table>
    </td>
    <td>B
      <br/>
      <table>
        <tr>
          <td>Bindings</td>
        </tr>
        <tr>
          <td>Budget</td>
        </tr>
      </table>
    </td>
    <td>C
      <br/>
      <table>
        <tr>
          <td>Cars</td>
        </tr>
        <tr>
          <td>Categories</td>
        </tr>
        <tr>
          <td>Costs</td>
        </tr>
      </table>
    </td>
    <td>P
      <br/>
      <table>
        <tr>
          <td>Policy</td>
        </tr>
        <tr>
          <td>Politics</td>
        </tr>
      </table>
    </td>
  </tr>
  <tr>
    <td>R
      <br/>
      <table>
        <tr>
          <td>Reevaluations</td>
        </tr>
        <tr>
          <td>Report</td>
        </tr>
      </table>
    </td>
  </tr>
</table>

Обратите внимание на три вещи :

  • Мы используем функцию расширения ( exslt ) ext: node-set () для преобразования промежуточного результат из RTF (Result-Fragment) во временное дерево.

  • <xsl:for-each select="$pDoc">, необходимый для того, чтобы исходный XML-документ снова стал текущим XML-документом, чтобы функция key () использовала индекс, созданный для этого документа, а не для временного дерева.

  • Каждая начальная буква, которая должна начинать новую строку (4) начальных букв, обрабатывается в специальном шаблоне, в котором создается <tr>. Затем эта и остальные (3) начальные буквы в строке обрабатываются в теле <tr> в режиме «копирования», просто создавая <td> каждая.

Здесь мы рассмотрели и продемонстрировали несколько передовых методов XSLT :

  • Использование оператора mod для группировки по количеству.
  • Использование функции () для документа, отличного от текущего.
  • Режимы
  • Преобразование RTF во временное дерево

Наслаждайтесь :)

0 голосов
/ 20 декабря 2008

Вот второе решение, которое не требует каких-либо функций расширения . Обратите внимание, что не является рекурсивным и может быть более эффективным, чем рекурсивный.

Это преобразование :

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:namespace"
 exclude-result-prefixes="my"
 >
 <xsl:output method="html"/>

  <my:alpha>
    <l>A</l><l>B</l><l>C</l><l>D</l><l>E</l>
    <l>F</l><l>G</l><l>H</l><l>I</l><l>J</l>
    <l>K</l><l>L</l><l>M</l><l>N</l><l>O</l>
    <l>P</l><l>Q</l><l>R</l><l>S</l><l>T</l>
    <l>U</l><l>V</l><l>W</l><l>X</l><l>Y</l>
    <l>Z</l>
  </my:alpha>
    <xsl:variable name="vDoc" select="/"/>
    <xsl:variable name="vNumCols" select="4"/>
    <xsl:variable name="vLower"  select="'abcdefghijklmnopqrstuvwxyz'"  />
    <xsl:variable name="vUpper"  select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"  />

    <xsl:key name="rows-by-FirstLetter" match="Row"
      use="translate(substring(@Title,1,1),
                     'abcdefghijklmnopqrstuvwxyz',
                     'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" />

  <xsl:variable name="vStartingLetters">
    <xsl:for-each select=
        "/*/*/Row
           [generate-id()
           =
            generate-id(key('rows-by-FirstLetter',
                             translate(substring(@Title,1,1),
                                                 $vLower,
                                                 $vUpper)
                             )[1]
                         )
            ]">
      <xsl:value-of select=
         "translate(substring(@Title,1,1),
                    $vLower,
                    $vUpper)"/>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="vMyLetters" select=
   "document('')/*/my:alpha/l[contains($vStartingLetters,.)]"
   />

    <xsl:template match="Rows">
        <table>
          <xsl:for-each select=
            "$vMyLetters[position() mod $vNumCols = 1]">
            <xsl:variable name="vPos"
               select="(position()-1)*$vNumCols+1"/>

              <tr>
                 <xsl:apply-templates select=
                  "$vMyLetters[position() >= $vPos
                              and
                               not(position() > $vPos+$vNumCols -1)
                              ]">
            <xsl:with-param name="pDoc" select="$vDoc"/>
                 </xsl:apply-templates>
              </tr>
             </xsl:for-each>
        </table>
    </xsl:template>

    <xsl:template match="l">
        <xsl:param name="pDoc"/>

        <xsl:variable name="pThis" select="."/>

        <td>
            <xsl:value-of select="."/>
            <br />
            <table>
                <xsl:for-each select="$pDoc">
                    <xsl:for-each select=
                            "key('rows-by-FirstLetter', $pThis)">
                <tr>
                  <td>

                    <xsl:value-of select="@Title"/>
                  </td>
                </tr>
                    </xsl:for-each>
                </xsl:for-each>
            </table>
        </td>
    </xsl:template>
</xsl:stylesheet>

при применении к следующему документу XML:

<dsQueryResponse>
    <Rows>
        <Row Title="Agenda" />
        <Row Title="Policy" />
        <Row Title="Policy" />
        <Row Title="Report" />
        <Row Title="Report" />
        <Row Title="Test2" />
        <Row Title="Test1" />
        <Row Title="Boo" />
        <Row Title="Foo" />
    </Rows>
</dsQueryResponse>

дает желаемый результат:


<table>
   <tr>
      <td>A<br><table>
            <tr>
               <td>Agenda</td>
            </tr>
         </table>
      </td>
      <td>B<br><table>
            <tr>
               <td>Boo</td>
            </tr>
         </table>
      </td>
      <td>F<br><table>
            <tr>
               <td>Foo</td>
            </tr>
         </table>
      </td>
      <td>P<br><table>
            <tr>
               <td>Policy</td>
            </tr>
            <tr>
               <td>Policy</td>
            </tr>
         </table>
      </td>
   </tr>
   <tr>
      <td>R<br><table>
            <tr>
               <td>Report</td>
            </tr>
            <tr>
               <td>Report</td>
            </tr>
         </table>
      </td>
      <td>T<br><table>
            <tr>
               <td>Test2</td>
            </tr>
            <tr>
               <td>Test1</td>
            </tr>
         </table>
      </td>
   </tr>
</table>

Обратите внимание , что большинство объяснений в моем первом ответе применимы и к этому решению, за исключением того, что здесь мы не используем моды.

0 голосов
/ 18 декабря 2008

Я немного смущен вопросом, но думаю, что вы ищете xsl: if test с комбинацией position () и mod

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...