XSLT 2.0带计数器按模板循环处理数据异常问题及实现方法
异常产生原因
该异常完全由XSLT 1.0与XSLT 2.0的类型系统、运算规则差异导致,原代码依赖XSLT 1.0的隐式类型转换逻辑,在2.0环境下完全失效:
- 原代码所有
xsl:param未声明类型,传参时未使用select属性直接传值,而是在xsl:with-param内嵌xsl:value-of构造内容。XSLT 1.0中该方式传递的是结果树片段(RTF),执行<=比较时处理器会自动将RTF转为数值类型对比,逻辑符合预期。 - XSLT 2.0基于强类型的XPath 2.0数据模型,上述写法传递的是临时文档节点,无显式类型声明时,
<=运算符对两个无类型操作数会默认按字符串字典序比较,而非数值比较:- count为1-8的个位数时,i和count的字符串形式均为单字符,字典序与数值序完全一致,运行结果正常。
- count=9时,所有1-8开头的多位数(10~89)字符串首字符编码均小于
9,比较结果恒为真,直到i增长到90时,字符串"90"字典序大于"9"才会停止递归,最终执行约90次,与观测现象一致。 - count≥10时,count字符串首字符为
1,i=2对应的字符串"2"首字符编码大于1,第一次递归后判断即不成立,仅执行1次;count≥20时首字符为2,i=3时比较不成立,仅执行2次,执行次数随count首位字符变化无规律。
XSLT 2.0 正确实现方案
XSLT 2.0原生支持整数序列遍历,无需手写递归实现固定次数循环,代码更简洁可靠;如果需要保留原有递归结构,必须显式声明参数类型,避免隐式转换错误。
推荐方案:序列遍历实现(无递归)
直接使用xsl:for-each遍历1到目标次数的整数序列,完全规避递归栈溢出、隐式类型转换问题:
<?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" version="2.0"> <xsl:template match="/"> <Klammer> <!-- 遍历1到13的整数序列,修改13即可调整循环次数 --> <xsl:for-each select="1 to 13"> <xsl:call-template name="myRepeatedTemplate"> <!-- 当前上下文项即为循环计数器值,直接传整数类型参数 --> <xsl:with-param name="number" select="." as="xs:integer"/> </xsl:call-template> </xsl:for-each> </Klammer> </xsl:template> <xsl:template name="myRepeatedTemplate"> <xsl:param name="number" as="xs:integer"/> <Testelement> <Testnumber> <xsl:value-of select="$number"/> </Testnumber> </Testelement> </xsl:template> </xsl:stylesheet>
递归逻辑修正方案
如果必须保留原有递归调用结构,需要给所有参数显式声明整数类型,传参时使用select直接传递数值表达式,禁止构造临时树节点:
<?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" version="2.0"> <xsl:template match="/"> <Klammer> <xsl:call-template name="selects"> <xsl:with-param name="i" select="1" as="xs:integer"/> <xsl:with-param name="count" select="13" as="xs:integer"/> </xsl:call-template> </Klammer> </xsl:template> <xsl:template name="selects"> <!-- 显式声明参数为整数类型,强制走数值比较逻辑 --> <xsl:param name="i" as="xs:integer"/> <xsl:param name="count" as="xs:integer"/> <xsl:if test="$i <= $count"> <xsl:call-template name="myRepeatedTemplate"> <xsl:with-param name="number" select="$i" as="xs:integer"/> </xsl:call-template> <!-- 直接传递数值计算结果,不嵌套xsl:value-of构造临时节点 --> <xsl:call-template name="selects"> <xsl:with-param name="i" select="$i + 1" as="xs:integer"/> <xsl:with-param name="count" select="$count" as="xs:integer"/> </xsl:call-template> </xsl:if> </xsl:template> <xsl:template name="myRepeatedTemplate"> <xsl:param name="number" as="xs:integer"/> <Testelement> <Testnumber> <xsl:value-of select="$number"/> </Testnumber> </Testelement> </xsl:template> </xsl:stylesheet>
内容的提问来源于stack exchange,提问作者Lennart Weitzenberger
相关产品推荐
相关产品推荐

