XSLT в HTML с простой группировкой XML-данных с использованием шаблонов вместо для каждого

Я возился с xslt с тех пор, как стал администратором sharepoint, он много использует xslt для отображения данных списка. Недавно я начал использовать его для преобразования результатов базы данных, которые я преобразовал в xml с помощью метода расширения. Я пытаюсь создать чистый HTML.

Моя первая попытка сработала нормально. Как бы то ни было, я использовал for-each повсюду, с тех пор я читал, что это плохой поступок. Я прочитал кучу материалов об использовании ключей, но я не мог этого понять или заставить это работать. Итак, я переписал эту таблицу стилей ниже на ту, которая находится под ней. Он использует шаблоны без for-each.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="/">


<html>
  <head>
    <link rel="Stylesheet" type="text/css" href="../styles/BoxReportStyle.css" />
  </head>
  <body>


    <span class="BoxReport">
      <h2>Checked Out Boxes by Department with Transaction History</h2>

      Count=<xsl:value-of select="count( /CheckedOutBoxes/row ) "/>

    <!-- Get the divisions, since we are groing to group by division-->
    <xsl:variable name="DivisionList" select="/CheckedOutBoxes/row[ not( Division = preceding-sibling::row/Division ) ]/Division" />

    <xsl:for-each select="$DivisionList">

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

      <h3>
        <xsl:value-of disable-output-escaping="yes" select="$DivisionName "/>
      </h3>

      <!-- Get the list of departments, so we can group by department -->
      <xsl:variable name="DepartmentList" select="/CheckedOutBoxes/row[ Division = $DivisionName and not( Department = preceding-sibling::row/Department) ]/Department" />

      <xsl:for-each select="$DepartmentList">
        <xsl:variable name="DepartmentName" select="." />

        <h4>
          <xsl:value-of disable-output-escaping="yes" select="$DepartmentName"/>
        </h4>

        <xsl:variable name="Rows" select="/CheckedOutBoxes/row[ Division = $DivisionName and Department = $DepartmentName ]" />

        <!-- Start displaying the checked out box information for this division and department -->
        <table>
          <th>Box Number</th>
          <th>Status Name</th>
          <th>Entry Date</th>
          <th>Description</th>

          <xsl:for-each select="$Rows">

            <tr>
              <td>
                <xsl:value-of select="BoxNumber"/>
              </td>
              <td>
                <xsl:value-of select="StatusName"/>
              </td>
              <td>
                <xsl:value-of select="EntryDate"/>
              </td>
              <td width="200px">
                <xsl:value-of disable-output-escaping="yes" select="Description"/>
              </td>

            </tr>

            <!-- Now display the transaction history if there is any-->
            <xsl:if test=" count( Transaction ) > 0 ">
              <tr>
                <td></td> <!-- One blank row to shift things over-->
                <td colspan="3">
                  <!-- Display transaction table-->
                  <table class="SubTable">
                    <th>Transaction Date</th>
                    <th>Requestor</th>
                    <th>Comments</th>

                    <xsl:for-each select="Transaction" >
                      <tr>
                        <td>
                          <xsl:value-of select="TransactionDate"/>
                        </td>
                        <td>
                          <xsl:value-of select="Requestor"/>
                        </td>
                        <td width="200px">
                          <xsl:value-of disable-output-escaping="yes" select="Comments"/>
                        </td>
                      </tr>
                    </xsl:for-each>
                  </table>
                </td>
              </tr>
            </xsl:if>
          </xsl:for-each>
        </table>

      </xsl:for-each>

    </xsl:for-each>
    </span>


  </body>

</html>


  </xsl:template>



</xsl:stylesheet>

Я переписал это так:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">



 <xsl:template match="/">
<html>
  <head>
    <link rel="Stylesheet" type="text/css" href="../styles/BoxReportStyle.css" />
  </head>
  <body>
    <span class="BoxReport">

      <h2>Checked Out Boxes by Department with Transaction History</h2>

      Count=<xsl:value-of select="count( /CheckedOutBoxes/row ) "/>

      <xsl:apply-templates mode="Division" select="/CheckedOutBoxes/row[ not( Division = preceding-sibling::row/Division ) ]"></xsl:apply-templates>


    </span>
  </body>
</html>
  </xsl:template>

 <xsl:template mode="Division" match="row">
<h3>
  <xsl:value-of select="Division" disable-output-escaping="yes"/>
</h3>

<xsl:variable name="DivisionName" select="Division" />

<xsl:apply-templates mode="Department" select="/CheckedOutBoxes/row[ Division = $DivisionName and not( Department = preceding-sibling::row/Department ) ]"></xsl:apply-templates>

</xsl:template>

 <xsl:template mode="Department" match="row">
<h4>
  <xsl:value-of select="Department" disable-output-escaping="yes"/>
</h4>

<xsl:variable name="DivisionName" select="Division" />
<xsl:variable name="DepartmentName" select="Department" />

<table>
  <th>Box Number</th>
  <th>Status Name</th>
  <th>Entry Date</th>
  <th>Description</th>

  <xsl:apply-templates mode="row" select="/CheckedOutBoxes/row[ Division = $DivisionName and Department = $DepartmentName ]" ></xsl:apply-templates>

  </table>



</xsl:template>

<xsl:template mode="row" match="row">

<tr>
  <td>
    <xsl:value-of select="BoxNumber"/>
  </td>
  <td>
    <xsl:value-of select="StatusName"/>
  </td>
  <td>
    <xsl:value-of select="EntryDate"/>
  </td>
  <td width="200px">
    <xsl:value-of disable-output-escaping="yes" select="Description"/>
  </td>

</tr>

<!-- Display Transaction stuff as another row if we have any -->
<xsl:if test=" count( Transaction ) > 0 ">
  <tr>
    <td></td><!-- Shift the transaction over-->
    <td colspan="3">
      <!-- Start Transaction Table -->
      <table class="SubTable">
        <th>Transaction Date</th>
        <th>Requestor</th>
        <th>Comments</th>

        <xsl:apply-templates select="Transaction">
          <xsl:sort order="descending" select="TransactionDate"/>
        </xsl:apply-templates>
      </table>
    </td>
  </tr>
</xsl:if>

</xsl:template>


<xsl:template match="Transaction">
<tr>
  <td>
    <xsl:value-of select="TransactionDate"/>
  </td>
  <td>
    <xsl:value-of select="Requestor"/>
  </td>
  <td width="200px">
    <xsl:value-of disable-output-escaping="yes" select="Comments"/>
  </td>
</tr>
  </xsl:template>

</xsl:stylesheet>

Я не включил образцы ввода и вывода, так как все они создаются автоматически. Если это необходимо, я могу потратить много времени и попробовать что-нибудь произвести.

Мой вопрос: это лучший способ сделать это? Также, если ключевой способ лучше, может ли кто-нибудь объяснить его или предоставить ссылку на хорошее объяснение?


person 249076    schedule 09.06.2010    source источник


Ответы (2)


В основном вопрос использования for-each по сравнению с шаблонами сводится к созданию многоразовых, более общих преобразований.

Используя шаблоны, все совпадающие узлы - не только те, которые явно используются в for-each - могут извлечь выгоду из шаблона, который помогает избежать дублирования кода и в то же время разбивает лист на более мелкие блоки, которыми легче управлять. На самом деле это почти то же самое, что при императивном программировании вызывать друг друга огромные процедуры или более мелкие процедуры.

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

Тем не менее, вы можете узнать о мюнхианском методе (который использует ключи ) для фактической группировки данных с повторяющимися ключами. Использование оси preceding-sibling на некоторых двигателях происходит очень медленно, поэтому лучше избегать этого, когда это не является абсолютно необходимым.

Что-то вроде этого должно помочь с делениями (не тестировалось):

<xsl:key name="divisions" match="/CheckedOutBoxes/row/Division" use="." />

...

<xsl:apply-templates mode="Division" select="/CheckedOutBoxes/Division[generate-id(.)=generate-id(key('divisions', .))]" />
person Lucero    schedule 09.06.2010
comment
Ключ получает все неповторяющиеся разделы? Может быть, я смогу лучше понять это, если это то, что он делает. Тогда я мог бы просто сделать еще один ключ для отдела, который в основном был таким же, как и подразделения. Тогда оператор выбора шаблона применения будет гарантировать, что я делаю отделы для текущего подразделения, а не для всех. - person 249076; 09.06.2010
comment
Функция key() в этом случае в основном возвращает набор узлов со всеми совпадающими Divisions. Магия здесь - это функция generate-id(), которая возвращает уникальный идентификационный идентификатор для первого узла в переданном ему наборе узлов. Следовательно, каждый Division идентификатор узла сравнивается с идентификатором первого узла с таким же содержимым (поэтому мы используем функцию key()), поэтому сопоставление будет выполняться только один раз для отдельного содержимого. - person Lucero; 09.06.2010
comment
Что ж, я думаю, после небольшой работы с шаблонами, чтобы сделать так, чтобы шаблоны Division и Department совпадали с Division и Department вместо row, и реализовав ключевые вещи, я получил это для работы вместо предыдущего брата. Я все это немного понимаю прямо сейчас, но, надеюсь, я начну лучше понимать это после изучения. - person 249076; 09.06.2010
comment
Почему вы используете абсолютное выражение XPath в качестве шаблона соответствия в <xsl:key>? Пожалуйста, исправьте на что-нибудь более рекомендуемое. - person Dimitre Novatchev; 09.06.2010
comment
@Dimitre, выкройку я взял так, чтобы связь с первоисточником была узнаваема. - person Lucero; 10.06.2010

Шаблон «для каждого» - хорошая особенность XSLT.

Совет использовать «шаблоны» вместо «для каждого» в основном касается возможного неправильного использования модели обработки XSLT.

В вашем примере все ясно: один наивный «шаблон» и множество «для каждого», которые направляют процесс.

Ключевое использование в самом XSLT связано с производительностью. Его полезность заключается в замене выражений XPath, которые включают повторяющееся перемещение многих узлов входного дерева. Мюнчианский метод группировки - это особое использование ключей. Простая группировка может быть оптимальной без использования ключей.

С другой стороны, население - это частный случай трансформации. Я думаю, что лучше отделить семантику XHTML от преобразования XSLT. В качестве примера посетите сайт www.aranedabienesraices.com.ar.

person Community    schedule 09.06.2010
comment
Я думаю, что вижу, где шаблоны, действительно разбивают вещи. Это почти как вызов функций. Таким образом, с совпадением / я могу видеть общий макет, затем я могу следить за применением шаблонов, чтобы пройти через преобразование. Я, наверное, сохраню обе таблицы стилей. - person 249076; 09.06.2010