我有一个XSLT样式表,它以错误的顺序调用模板。它不是按照它们出现的顺序呈现段落和表格,而是呈现所有段落,然后是所有表格。我猜这个问题可以通过用模式匹配替换条件逻辑来解决,但是我很难搞清楚如何做到这一点。
完整的XSLT在这里:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output indent="yes" method="html"/>
<xsl:template match="/*">
<html>
<head></head>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="richtext">
<xsl:if test="par">
<xsl:apply-templates select="." mode="par" />
</xsl:if>
<xsl:if test="table">
<xsl:apply-templates select="." mode="table" />
</xsl:if>
</xsl:template>
<xsl:template match="richtext" mode="par">
<xsl:for-each-group select="par[run[normalize-space()]]" group-adjacent="if (@def) then @def else preceding-sibling::par[run[normalize-space()]][@def][1]/@def">
<xsl:variable name="listType" select="preceding-sibling::*[1][self::pardef]/@list" />
<xsl:choose>
<xsl:when test="$listType = 'bullet'">
<ul>
<xsl:apply-templates select="current-group()" mode="list"/>
</ul>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()" mode="para" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</xsl:template>
<xsl:template match="richtext" mode="table">
<table border="1">
<xsl:for-each select="table/tablerow">
<tr>
<xsl:for-each select="tablecell">
<td>
<xsl:apply-templates />
</td>
</xsl:for-each>
</tr>
</xsl:for-each>
</table>
</xsl:template>
<xsl:template match="par" mode="list">
<li>
<xsl:apply-templates select="run" />
</li>
</xsl:template>
<xsl:template match="par" mode="para">
<p>
<xsl:apply-templates select="run" />
</p>
</xsl:template>
<xsl:template match="run">
<xsl:value-of select="text()" separator=""/>
</xsl:template>
</xsl:stylesheet>
这是XML:
<?xml version="1.0" encoding="UTF-8"?>
<document>
<item name="Some richtext">
<richtext>
<pardef/>
<par def="20">
<run>This is a </run>
<run>paragraph.</run>
</par>
<table>
<tablerow>
<tablecell>
<par def="43"><run>This is a table</run></par></tablecell>
<tablecell>
<par def="44"><run>This is some data</run></par></tablecell>
</tablerow>
</table>
<pardef id="21" list="bullet"/>
<par def="21">
<run>This is a </run>
<run>bullet point.</run>
</par>
<table>
<tablerow>
<tablecell>
<par def="43"><run>This is another table</run></par></tablecell>
<tablecell>
<par def="44"><run>This is some data</run></par></tablecell>
</tablerow>
</table>
</richtext>
</item>
</document>
这是我想要的输出:
<html>
<head></head>
<body>
<p>This is a paragraph.</p>
<table border="1">
<tr>
<td>This is a table</td>
<td>This is some data</td>
</tr>
</table>
<ul>
<li>This is a bullet point.</li>
</ul>
<table border="1">
<tr>
<td>This is another table</td>
<td>This is some data</td>
</tr>
</table>
</body>
</html>
答案 0 :(得分:2)
看起来很多问题源于尝试使用@mode来完成@match的工作。一个好的经验法则是避免使用@mode,除非您可能多次处理相同的节点或节点类型,或者想要在不同的时间执行不同的操作。
正如@ michael.hor257k所说,你最好让应用模板为你做逻辑选择。
此外,您的测试有点不稳定,希望看看是否有富文本的表/ par子,而不是当前上下文节点(这是apply-templates已经为您做的):xsl:if对你的例子来说,这些陈述都是正确的。
以下是一些XSLT,它将根据您的输入创建您想要的内容(使用XHTML而不是HTML):我最终使用模式来区分列表和正常段落,但这符合以上经验法则:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:local="http://example.com/local"
exclude-result-prefixes="xs local"
version="2.0">
<xsl:output indent="yes" method="xhtml"/>
<xsl:strip-space elements="*"/>
<xsl:key name="pars" match="par" use="preceding-sibling::pardef[1]/generate-id()"/>
<xsl:template match="document">
<html>
<head/>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<!-- I want to handle par using modes, so I deliberately remove any output in the default mode. We'll make an exception for par in table later. -->
<xsl:template match="par"/>
<xsl:template match="pardef[not(@list)]">
<!-- I'm using a key() function here just to easily return the par elements that follow this pardef
- see the xsl:key definition above. You could also try using @id and @def e.g.
<key name="pars" match="par" use="@def"/>
and key('pars', @id) - although the first @id in your example seems to be missing!
-->
<xsl:apply-templates select="key('pars', generate-id())" mode="p"/>
</xsl:template>
<xsl:template match="pardef[@list]">
<ul>
<xsl:apply-templates select="key('pars', generate-id())" mode="li"/>
</ul>
</xsl:template>
<xsl:template match="par" mode="p">
<p>
<!-- Any further elements don't need mode handling, so we'll return to the default mode -->
<xsl:apply-templates mode="#default"/>
</p>
</xsl:template>
<xsl:template match="par" mode="li">
<li>
<xsl:apply-templates mode="#default"/>
</li>
</xsl:template>
<xsl:template match="table">
<table border="1">
<xsl:apply-templates/>
</table>
</xsl:template>
<xsl:template match="tablerow">
<tr>
<xsl:apply-templates/>
</tr>
</xsl:template>
<xsl:template match="tablecell/par">
<td>
<xsl:apply-templates/>
</td>
</xsl:template>
</xsl:stylesheet>
如果有人能告诉我为什么我必须使用generate-id()而不仅仅是。在关键功能和定义中,我想知道 - 我认为这可能是我的Saxon(9.6.0.7)中的一个错误。