XSLT 中 <xsl:apply-templates> 元素详解
<xsl:apply-templates> 是 XSLT 中最核心、最常用的指令之一,它体现了 XSLT “基于模板规则(template rules)”的声明式编程思想。
基本语法
<xsl:apply-templates
select="表达式"
mode="模式名称">
<xsl:sort /> <!-- 可选的排序 -->
<xsl:with-param /> <!-- 向被调用的模板传递参数 -->
</xsl:apply-templates>
主要属性
| 属性 | 是否必须 | 说明 | 示例 |
|---|---|---|---|
select | 否 | 指定要处理哪些节点。如果省略,默认处理当前节点的全部子节点(按文档顺序) | select="book"、select="//item" |
mode | 否 | 用于区分同一节点在不同场景下使用不同的模板,防止模板无限递归或冲突 | mode="toc"、mode="content" |
工作原理(重点)
- XSLT 处理器在遇到
<xsl:apply-templates>时,会根据当前上下文节点去查找最匹配、最优先的<xsl:template match="...">。 - 查找顺序遵循 XPath 的匹配规则和 XSLT 的导入优先级(import precedence)与模板优先级(priority)。
- 找到匹配的模板后,将
select选出的节点逐个作为当前节点传入该模板执行。 - 如果有
mode属性,只会匹配同样带有相同mode的模板。
常见用法示例
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- 默认处理所有书 -->
<xsl:template match="book">
<div class="book">
<h2><xsl:value-of select="title"/></h2>
<xsl:apply-templates select="author | year"/> <!-- 只处理 author 和 year -->
</div>
</xsl:template>
<!-- 单独处理作者 -->
<xsl:template match="author">
<p>作者:<xsl:value-of select="."/></p>
</xsl:template>
<!-- 在目录模式下只输出标题 -->
<xsl:template match="book" mode="toc">
<li><xsl:value-of select="title"/></li>
</xsl:template>
<!-- 主模板 -->
<xsl:template match="/library">
<html>
<body>
<h1>目录</h1>
<ul>
<xsl:apply-templates select="book" mode="toc"/> <!-- 使用 toc 模式 -->
</ul>
<h1>详细内容</h1>
<xsl:apply-templates/> <!-- 省略 select,默认处理所有子节点(即所有 book) -->
</body>
</html>
</xsl:template>
</xsl:stylesheet>
与 <xsl:call-template> 的区别(常考点)
| 特性 | <xsl:apply-templates> | <xsl:call-template> |
|---|---|---|
| 调用方式 | 根据节点动态匹配模板 | 直接按名字调用指定模板 |
| 传入节点 | 会把选中的节点逐个作为当前节点传入 | 不会改变当前节点 |
| 是否支持多态(同一节点不同处理) | 支持(配合 mode) | 不支持 |
| 是否需要参数传递 | 通常不需要(节点本身就是上下文) | 经常需要用 <xsl:with-param> 传参 |
| 典型场景 | 遍历、递归、内容驱动的转换 | 抽取公共代码块、函数式调用 |
常见的“陷阱”和最佳实践
- 忘记写匹配文本节点的模板
默认情况下,文本节点也会被<xsl:apply-templates/>处理,如果没有匹配模板,就会直接输出(常导致多余空格)。
解决方法:
<xsl:template match="text()"/> <!-- 抑制所有纯文本输出 -->
或
<xsl:template match="text()[normalize-space(.)='']"/> <!-- 只抑制空白文本 -->
- 无限递归
常见于写了<xsl:template match="item"><xsl:apply-templates/></xsl:template>却没有更具体的模板,导致一直匹配自己。
用mode或更精确的match表达式避免。 - 性能问题
select="//item"这种全文档搜索很慢,尽量使用相对路径或索引。 - 排序
可以在<xsl:apply-templates>内部使用<xsl:sort>:
<xsl:apply-templates select="book">
<xsl:sort select="year" data-type="number" order="descending"/>
<xsl:sort select="title"/>
</xsl:apply-templates>
总结一句话
<xsl:apply-templates> 是 XSLT 的“灵魂”,它让转换过程由数据驱动(push 风格 → pull 风格),而不是硬编码的顺序处理,是实现“一次编写,多种输出格式”的关键。
如果你有具体的转换场景需要用 <xsl:apply-templates> 实现,可以贴出 XML 和期望的输出,我可以帮你写出完整样式表。