意外的 <xsl:apply-imports/> 行为
Unanticipated <xsl:apply-imports/> behavior
我一直在努力弄清楚如何最好地模块化我的 XSLT 样式表以促进重用。我突然想到使用 作为将特定于文档的属性引入标准标记转换的方法。这 不是 按我预期的方式工作,我什至无法开始理解这里发生的事情。这是样式表的简化版本:
<!-- main.xsl -->
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:import href="html-customizations.xsl"/>
<xsl:output method="xml"
indent="yes"
omit-xml-declaration="no"/>
<xsl:template match="para">
<fo:block>
<xsl:attribute name="space-after">1em</xsl:attribute>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<!-- =============== -->
<!-- Inline Elements -->
<!-- =============== -->
<xsl:template match="i">
<fo:inline font-style="italic">
<xsl:apply-imports/>
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
<!-- ================ -->
<!-- Tables -->
<!-- ================ -->
<xsl:template match="table">
<fo:table>
<xsl:apply-imports/>
<xsl:apply-templates/>
</fo:table>
</xsl:template>
<xsl:template match="tr">
<fo:table-row>
<xsl:apply-imports/>
<xsl:apply-templates/>
</fo:table-row>
</xsl:template>
<xsl:template match="td | th">
<fo:table-cell>
<xsl:apply-imports/>
<xsl:apply-templates/>
</fo:table-cell>
</xsl:template>
</xsl:stylesheet>
导入的样式表:
<!-- html-customizations.xsl -->
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="td | th">
<xsl:attribute name="hyphenate">true</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
这是 XML 输入文件:
<!-- test.xml -->
<para>
<table>
<tr><td>Spongebob Squarepants, <i>Chair</i></td></tr>
<tr><td>Patrick Starfish, <i>Vice Cchair</i></td></tr>
<tr><td>Squidword, <i>Secretary</i></td></tr>
</table>
</para>
$
xalan -o out.xml test.xml main.xsl
out.xml:
<?xml version="1.0" encoding="UTF-8"?>
<fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format" space-after="1em">
<fo:table>
<fo:table-row>
<fo:table-cell hyphenate="true">Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>
</fo:table-cell>
<fo:table-cell hyphenate="true">Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell hyphenate="true">Patrick Starfish, <fo:inline font-style="italic">Vice CchairVice Cchair</fo:inline>
</fo:table-cell>
<fo:table-cell hyphenate="true">Patrick Starfish, <fo:inline font-style="italic">Vice CchairVice Cchair</fo:inline>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell hyphenate="true">Squidword, <fo:inline font-style="italic">SecretarySecretary</fo:inline>
</fo:table-cell>
<fo:table-cell hyphenate="true">Squidword, <fo:inline font-style="italic">SecretarySecretary</fo:inline>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell hyphenate="true">Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>
...
...
如您所见,与包含 的模板匹配的元素的每个子元素都被重复了!我包含了导入的样式表以说明我正在尝试做什么。如果我注释掉这个导入:
<!--
<xsl:import href="html-customizations.xsl"/>
-->
重复行为相同:
<?xml version="1.0" encoding="UTF-8"?>
<fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format" space-after="1em">
<fo:table>
<fo:table-row>
<fo:table-cell>Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>
</fo:table-cell>
<fo:table-cell>Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
...
...
没有我试图从导入的样式表中添加的属性;即只是 处理指令的 presence 导致输出元素加倍。另请注意,这不仅仅是 xalan 问题——同样的事情发生在 MSXML on Windows 7.
上
有什么想法吗?我指望这项工作,所以现在我急于想弄清楚如何解决这个问题。
顺便说一句,我对如何使用 的假设是基于 Michael Kay 书中 xsl:import 部分给出的示例。如果有人知道解释我在上面看到的行为的参考资料,请分享。
我同意 apply-imports
的行为很难理解。问题是 apply-imports
总是 找到与当前节点匹配的模板,即使用户没有定义它。在这种情况下,默认 模板适用。
以下样式表有效:
XSLT 样式表
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:import href="html-customizations.xsl"/>
<xsl:output method="xml"
indent="yes"
omit-xml-declaration="no"/>
<xsl:template match="para">
<fo:block>
<xsl:attribute name="space-after">1em</xsl:attribute>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<!-- =============== -->
<!-- Inline Elements -->
<!-- =============== -->
<xsl:template match="i">
<fo:inline font-style="italic">
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
<!-- ================ -->
<!-- Tables -->
<!-- ================ -->
<xsl:template match="table">
<fo:table>
<xsl:apply-templates/>
</fo:table>
</xsl:template>
<xsl:template match="tr">
<fo:table-row>
<xsl:apply-templates/>
</fo:table-row>
</xsl:template>
<xsl:template match="td | th">
<fo:table-cell>
<xsl:apply-imports/>
<xsl:apply-templates/>
</fo:table-cell>
</xsl:template>
</xsl:stylesheet>
如您所见,我删除了两个 apply-imports
元素,只留下 template/@match='td | th'
中的一个。然后,输出将是
XML输出
<?xml version="1.0" encoding="UTF-8"?>
<fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format" space-after="1em">
<fo:table>
<fo:table-row>
<fo:table-cell hyphenate="true">Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell hyphenate="true">Patrick Starfish, <fo:inline font-style="italic">Vice CchairVice Cchair</fo:inline>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell hyphenate="true">Squidword, <fo:inline font-style="italic">SecretarySecretary</fo:inline>
</fo:table-cell>
</fo:table-row>
</fo:table>
</fo:block>
到底发生了什么?
apply-imports
寻找一个模板
- 匹配当前节点
- 匹配当前模式
- 在导入的样式表中
现在,关键是:如果在导入的样式表中找不到这样的模板,则此指令将调用 built-in templates。在tr
的情况下:
<xsl:template match="tr">
<fo:table-row>
<xsl:apply-imports/>
<xsl:apply-templates/>
</fo:table-row>
</xsl:template>
元素节点的默认操作是遍历它并将模板应用于其内容,因此上面的代码片段实际上转换为
<xsl:template match="tr">
<fo:table-row>
<xsl:apply-templates/>
<xsl:apply-templates/>
</fo:table-row>
</xsl:template>
这就是输出包含重复项的原因。我想你现在也明白为什么注释掉 xsl:import
没有帮助,否则我很乐意详细说明。
由于您还要求提供参考,因此 在 Michael Kay 的 XSLT 2.0 和 XPath 2.0 程序员参考 中进行了解释,第 238 页。
我一直在努力弄清楚如何最好地模块化我的 XSLT 样式表以促进重用。我突然想到使用
<!-- main.xsl -->
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:import href="html-customizations.xsl"/>
<xsl:output method="xml"
indent="yes"
omit-xml-declaration="no"/>
<xsl:template match="para">
<fo:block>
<xsl:attribute name="space-after">1em</xsl:attribute>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<!-- =============== -->
<!-- Inline Elements -->
<!-- =============== -->
<xsl:template match="i">
<fo:inline font-style="italic">
<xsl:apply-imports/>
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
<!-- ================ -->
<!-- Tables -->
<!-- ================ -->
<xsl:template match="table">
<fo:table>
<xsl:apply-imports/>
<xsl:apply-templates/>
</fo:table>
</xsl:template>
<xsl:template match="tr">
<fo:table-row>
<xsl:apply-imports/>
<xsl:apply-templates/>
</fo:table-row>
</xsl:template>
<xsl:template match="td | th">
<fo:table-cell>
<xsl:apply-imports/>
<xsl:apply-templates/>
</fo:table-cell>
</xsl:template>
</xsl:stylesheet>
导入的样式表:
<!-- html-customizations.xsl -->
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="td | th">
<xsl:attribute name="hyphenate">true</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
这是 XML 输入文件:
<!-- test.xml -->
<para>
<table>
<tr><td>Spongebob Squarepants, <i>Chair</i></td></tr>
<tr><td>Patrick Starfish, <i>Vice Cchair</i></td></tr>
<tr><td>Squidword, <i>Secretary</i></td></tr>
</table>
</para>
$ xalan -o out.xml test.xml main.xsl
out.xml:
<?xml version="1.0" encoding="UTF-8"?>
<fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format" space-after="1em">
<fo:table>
<fo:table-row>
<fo:table-cell hyphenate="true">Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>
</fo:table-cell>
<fo:table-cell hyphenate="true">Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell hyphenate="true">Patrick Starfish, <fo:inline font-style="italic">Vice CchairVice Cchair</fo:inline>
</fo:table-cell>
<fo:table-cell hyphenate="true">Patrick Starfish, <fo:inline font-style="italic">Vice CchairVice Cchair</fo:inline>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell hyphenate="true">Squidword, <fo:inline font-style="italic">SecretarySecretary</fo:inline>
</fo:table-cell>
<fo:table-cell hyphenate="true">Squidword, <fo:inline font-style="italic">SecretarySecretary</fo:inline>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell hyphenate="true">Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>
...
...
如您所见,与包含
<!--
<xsl:import href="html-customizations.xsl"/>
-->
重复行为相同:
<?xml version="1.0" encoding="UTF-8"?>
<fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format" space-after="1em">
<fo:table>
<fo:table-row>
<fo:table-cell>Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>
</fo:table-cell>
<fo:table-cell>Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
...
...
没有我试图从导入的样式表中添加的属性;即只是
有什么想法吗?我指望这项工作,所以现在我急于想弄清楚如何解决这个问题。
顺便说一句,我对如何使用
我同意 apply-imports
的行为很难理解。问题是 apply-imports
总是 找到与当前节点匹配的模板,即使用户没有定义它。在这种情况下,默认 模板适用。
以下样式表有效:
XSLT 样式表
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:import href="html-customizations.xsl"/>
<xsl:output method="xml"
indent="yes"
omit-xml-declaration="no"/>
<xsl:template match="para">
<fo:block>
<xsl:attribute name="space-after">1em</xsl:attribute>
<xsl:apply-templates/>
</fo:block>
</xsl:template>
<!-- =============== -->
<!-- Inline Elements -->
<!-- =============== -->
<xsl:template match="i">
<fo:inline font-style="italic">
<xsl:apply-templates/>
</fo:inline>
</xsl:template>
<!-- ================ -->
<!-- Tables -->
<!-- ================ -->
<xsl:template match="table">
<fo:table>
<xsl:apply-templates/>
</fo:table>
</xsl:template>
<xsl:template match="tr">
<fo:table-row>
<xsl:apply-templates/>
</fo:table-row>
</xsl:template>
<xsl:template match="td | th">
<fo:table-cell>
<xsl:apply-imports/>
<xsl:apply-templates/>
</fo:table-cell>
</xsl:template>
</xsl:stylesheet>
如您所见,我删除了两个 apply-imports
元素,只留下 template/@match='td | th'
中的一个。然后,输出将是
XML输出
<?xml version="1.0" encoding="UTF-8"?>
<fo:block xmlns:fo="http://www.w3.org/1999/XSL/Format" space-after="1em">
<fo:table>
<fo:table-row>
<fo:table-cell hyphenate="true">Spongebob Squarepants, <fo:inline font-style="italic">ChairChair</fo:inline>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell hyphenate="true">Patrick Starfish, <fo:inline font-style="italic">Vice CchairVice Cchair</fo:inline>
</fo:table-cell>
</fo:table-row>
<fo:table-row>
<fo:table-cell hyphenate="true">Squidword, <fo:inline font-style="italic">SecretarySecretary</fo:inline>
</fo:table-cell>
</fo:table-row>
</fo:table>
</fo:block>
到底发生了什么?
apply-imports
寻找一个模板
- 匹配当前节点
- 匹配当前模式
- 在导入的样式表中
现在,关键是:如果在导入的样式表中找不到这样的模板,则此指令将调用 built-in templates。在tr
的情况下:
<xsl:template match="tr">
<fo:table-row>
<xsl:apply-imports/>
<xsl:apply-templates/>
</fo:table-row>
</xsl:template>
元素节点的默认操作是遍历它并将模板应用于其内容,因此上面的代码片段实际上转换为
<xsl:template match="tr">
<fo:table-row>
<xsl:apply-templates/>
<xsl:apply-templates/>
</fo:table-row>
</xsl:template>
这就是输出包含重复项的原因。我想你现在也明白为什么注释掉 xsl:import
没有帮助,否则我很乐意详细说明。
由于您还要求提供参考,因此 在 Michael Kay 的 XSLT 2.0 和 XPath 2.0 程序员参考 中进行了解释,第 238 页。