为何带mode="Items"的apply-templates选//z:row返回空,copy-of正常返回节点集
行为差异的根本原因
首先明确核心前提:三个样式表中使用的XPath表达式//z:row本身完全正确,不存在路径错误、命名空间不匹配的问题。可以佐证的现象是:样式表#2通过copy-of能正常返回两个目标z:row节点,样式表#3用同一表达式的apply-templates也能生成带正确属性值的Item节点。
单独运行样式表#1输出空的本质原因,是当前使用的XSLT处理器(SharePoint场景默认搭载的老版本MSXML引擎)存在懒加载优化的兼容性bug:
- 当处理器在模板内仅执行单次
apply-templates调用、且没有其他操作强制遍历深层DOM节点时,由于输入XML是多层无命名空间嵌套结构,最内层的listitems块又绑定了默认命名空间,处理器的浅解析逻辑会无法穿透外层的NewDataSet/Contracts节点链,在第一次执行//z:row查询时返回空节点集,apply-templates没有可处理的内容,最终输出全空。 - 当同一模板内同时存在
copy-of对同一节点集的查询时,处理器会在编译阶段预先执行完整DOM遍历,把深层节点加载到内存中,此时apply-templates再执行查询就能拿到正确的节点集。这也是为什么样式表#3能正常输出Item节点、同时附带copyof生成的原始z:row节点。
调整方案
要实现仅输出两个Item节点的预期目标,不需要保留copy-of的原始节点输出,只要通过显式遍历触发处理器的DOM加载逻辑即可。可选择两种调整方式:
- 最简洁可靠的方式:放弃依赖存在兼容问题的apply-templates模式匹配逻辑,改用
xsl:for-each直接遍历z:row节点生成目标结构。xsl:for-each本身会强制处理器完整遍历指定节点集,从根本上绕开懒加载bug。调整后的核心模板代码为:
<xsl:template match="/"> <Items xmlns:z="#RowsetSchema"> <xsl:for-each select="//z:row"> <Item ows_Title="{@ows_Title}" /> </xsl:for-each> </Items> </xsl:template>
- 如果一定要保留原有apply-templates的写法,只需要在apply-templates前加入一句不产生任何输出的强制节点遍历语句,触发DOM加载即可:
<xsl:template match="/"> <Items xmlns:z="#RowsetSchema"> <!-- 强制预加载节点集,解决懒加载导致的空结果问题 --> <xsl:variable name="preload" select="//z:row"/> <xsl:apply-templates select="//z:row" mode="Items" /> </Items> </xsl:template>
两种调整方式的最终输出完全一致:仅包含Items根节点,以及两个带正确ows_Title属性的Item节点,不会附带多余的原始z:row节点。
内容的提问来源于stack exchange,提问作者fvargas
相关产品推荐
相关产品推荐

