如何应用两个模板并按大小对结果进行分组并使用 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() < 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: absolute
和 nth-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() < $pageSize]" />
<!-- split into rows -->
<xsl:for-each select="$thePage[position() mod $cols = 1]">
<xsl:variable name="theRow"
select=". | following-sibling::card[position() < $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() < $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 > 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>
我正在尝试解析 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() < 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
.
.back .card:nth-child(1) {
/* this is card2 */
}
话虽这么说,您也可以通过将 position: absolute
和 nth-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() < $pageSize]" />
<!-- split into rows -->
<xsl:for-each select="$thePage[position() mod $cols = 1]">
<xsl:variable name="theRow"
select=". | following-sibling::card[position() < $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() < $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 > 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>