如何应用两个模板并按大小对结果进行分组并使用 xslt 模板

How to apply two templates and group the result by size and used template with xslt

我正在尝试解析 XML 并生成可用于打印的 HTML。

XML 中的元素内容以卡片的形式呈现,并具有正面和背面的信息。一张纸可以放八张卡片。 为了让 HTML/CSS 世界中的定位更轻松,我想按如下方式预先安排 XSLT 输出: 对于输入中的 16 个元素,我希望首先获得前 8 个元素的正面输出,然后以不同的顺序输出前 8 个元素的背面输出,以便在使用双面打印时它们与相应的正面相匹配,然后是接下来的 8 个元素的前端输出等

背面的顺序是这样的:前半部分倒序,后半部分倒序。前任。背面卡片 4, 3, 2, 1, 8, 7, 6, 5.

或者对于以下输入(为了简单起见,我在示例中每页只使用 4 张卡片):

<cards>
  <card>
    <question>First for the front</question>
    <answer>First to the back</answer>
  </card>
  <card>
    <question>Second for the front</question>
    <answer>Second to the back</answer>
  </card>
  <card>
    <question>Third for the front</question>
    <answer>Third to the back</answer>
  </card>
  <card>
    <question>Fourth for the front</question>
    <answer>Fourth to the back</answer>
  </card>
  <card>
    <question>Fifth for the front</question>
    <answer>Fifth to the back</answer>
  </card>
</cards>

我想获得以下输出:

<html><body>
   <div>
     <div class="card1 front">First for the front</div>
     <div class="card2 front">Second for the front</div>
     <div class="card3 front">Third for the front</div>
     <div class="card4 front">Fourth for the front</div>
   </div>
   <div>
     <div class="card2 back">Second to the back</div>
     <div class="card1 back">First to the back</div>
     <div class="card4 back">Fourth to the back</div>
     <div class="card3 back">Third to the back</div>
   </div>
   <div>
     <div class="card1 front">Fifth for the front</div>
   </div>
   <div>
     <div class="card1 back">Fifth to the back</div>
   </div>
 </body></html>

我有一个想法,在下一场比赛和模式中处理两次元素,但我不知道如何将它与我想申请的 sorting/grouping 结合起来。

如果能指出正确的方向,我将不胜感激。

此 XSLT 1.0 转换实现了所需的结构:

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="html" encoding="UTF-8" indent="yes" />

  <xsl:template match="/cards">
    <html>
      <body>
        <xsl:for-each select="card[position() mod 4 = 1]">
          <xsl:variable name="thisPage" select="
            . | following-sibling::card[position() &lt; 4]
          " />
          <div class="front">
            <xsl:apply-templates select="$thisPage/question" />
          </div>
          <div class="back">
            <xsl:apply-templates select="$thisPage/answer">
              <xsl:sort select="substring('2143', position(), 1)" data-type="number" />
            </xsl:apply-templates>
          </div>
        </xsl:for-each>
      </body>
    </html>
  </xsl:template>

  <xsl:template match="card/*">
    <div class="card">
      <xsl:value-of select="normalize-space()" />
    </div>
  </xsl:template>
</xsl:transform>

我认为您真的 不需要CSS class 编号的名称。添加它们会使事情变得不必要的复杂。

如有必要,您可以使用 nth-child.

在 CSS 样式表中轻松实现相同的目的
.back .card:nth-child(1) {
    /* this is card2 */
}

话虽这么说,您也可以通过将 position: absolutenth-child 组合在 CSS。这样就不需要在 XSLT 中进行特殊排序了。

让这个变得通用 - 我们希望每页 $rows$cols 卡片,并且在答案侧需要打印每个 以相反的顺序。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

  <xsl:param name="rows" select="2" />
  <xsl:param name="cols" select="2" />
  <xsl:variable name="pageSize" select="$rows * $cols" />

  <xsl:template match="/">
    <html>
      <body>
        <xsl:apply-templates select="cards/card[position() mod $pageSize = 1]" />
      </body>
    </html>
  </xsl:template>

  <xsl:template match="card">
    <!-- fronts -->
    <div>
      <xsl:call-template name="printRows">
        <xsl:with-param name="element" select="1"/>
        <xsl:with-param name="order" select="'ascending'" />
      </xsl:call-template>
    </div>
    <!-- backs -->
    <div>
      <xsl:call-template name="printRows">
        <xsl:with-param name="element" select="2"/>
        <xsl:with-param name="order" select="'descending'" />
      </xsl:call-template>
    </div>
  </xsl:template>

  <xsl:template name="printRows">
    <xsl:param name="element" />
    <xsl:param name="order" />
    <xsl:variable name="thePage"
        select=". | following-sibling::card[position() &lt; $pageSize]" />
    <!-- split into rows -->
    <xsl:for-each select="$thePage[position() mod $cols = 1]">
      <xsl:variable name="theRow"
          select=". | following-sibling::card[position() &lt; $cols]" />
      <!-- and process each row in appropriate order -->
      <xsl:apply-templates select="$theRow/*[$element]">
        <xsl:sort select="position()" order="{$order}" data-type="number" />
      </xsl:apply-templates>
    </xsl:for-each>
  </xsl:template>

  <xsl:template match="question">
    <div class="card{(count(../preceding-sibling::card) mod $pageSize) + 1} front">
      <xsl:value-of select="." />
    </div>
  </xsl:template>

  <xsl:template match="answer">
    <div class="card{(count(../preceding-sibling::card) mod $pageSize) + 1} back">
      <xsl:value-of select="." />
    </div>
  </xsl:template>
</xsl:stylesheet>

这适用于任何网格大小,只需通过前两个参数设置(或传入)行数和列数。为了计算 class 名称,我计算了前面的兄弟姐妹,因此数字基于原始文档顺序中的全套 card 元素 - position() 函数将给出 已排序 位置,并且仅在当前行内。

但是,我认为您的输出逻辑可能存在缺陷 - 在最后(短)行中,卡片 5 的背面应该在 second 列而不是第一个,以便打印在其各自正面的对面。一般来说,如果 "back" 行中的卡片少于 $cols ,那么您需要用空 div 向左填充它以使所有内容对齐。一种方法是像这样修改 printRows 模板:

<!-- split into rows -->
<xsl:for-each select="$thePage[position() mod $cols = 1]">
  <xsl:variable name="theRow"
      select=". | following-sibling::card[position() &lt; $cols]" />

  <xsl:if test="$order = 'descending'">
    <!--
    special case for a short reverse row (i.e. the last row of the last page) - we need
    to left-pad this row with empty columns to get the right positioning
    -->
    <xsl:call-template name="emptyDivs">
      <xsl:with-param name="num" select="$cols - count($theRow)" />
    </xsl:call-template>
  </xsl:if>

  <!-- and process each row in appropriate order -->
  <xsl:apply-templates select="$theRow/*[$element]">
    <xsl:sort select="position()" order="{$order}" data-type="number" />
  </xsl:apply-templates>
</xsl:for-each>

emptyDivs模板所在的位置

<xsl:template name="emptyDivs">
  <xsl:param name="num"/>
  <xsl:if test="$num &gt; 0">
    <div class="padding back" />
    <xsl:call-template name="emptyDivs">
      <xsl:with-param name="num" select="$num - 1" />
    </xsl:call-template>
  </xsl:if>
</xsl:template>

这将为两行两列的页面生成以下输出:

<html>
  <body>
    <div>
      <div class="card1 front">First for the front</div>
      <div class="card2 front">Second for the front</div>
      <div class="card3 front">Third for the front</div>
      <div class="card4 front">Fourth for the front</div>
    </div>
    <div>
      <div class="card2 back">Second to the back</div>
      <div class="card1 back">First to the back</div>
      <div class="card4 back">Fourth to the back</div>
      <div class="card3 back">Third to the back</div>
    </div>
    <div>
      <div class="card1 front">Fifth for the front</div>
    </div>
    <div>
      <div class="padding back"></div>
      <div class="card1 back">Fifth to the back</div>
    </div>
  </body>
</html>

或以下两行三列:

<html>
  <body>
    <div>
      <div class="card1 front">First for the front</div>
      <div class="card2 front">Second for the front</div>
      <div class="card3 front">Third for the front</div>
      <div class="card4 front">Fourth for the front</div>
      <div class="card5 front">Fifth for the front</div>
    </div>
    <div>
      <div class="card3 back">Third to the back</div>
      <div class="card2 back">Second to the back</div>
      <div class="card1 back">First to the back</div>
      <div class="padding back"></div>
      <div class="card5 back">Fifth to the back</div>
      <div class="card4 back">Fourth to the back</div>
    </div>
  </body>
</html>