如何在XSLT模板中忽略与<code>标签缩进对应数量的起始制表符?
解决XML中
<code>标签代码缩进冲突的XSLT方案 实现思路
要搞定XML缩进和<code>内代码的冲突,分两步来:
- 先数清楚
<code>起始标签前面有多少个制表符(记为n) - 把
<code>里每一行开头的n个制表符删掉,同时去掉内容首尾的换行符
完整XSLT代码
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 匹配code标签并处理 --> <xsl:template match="code"> <pre> <xsl:call-template name="clean-code-content"> <!-- 传入code的文本内容,先清理首尾空白 --> <xsl:with-param name="raw-text" select="normalize-space(., ' \t')"/> <!-- 计算code标签前的制表符数量 --> <xsl:with-param name="indent-num"> <xsl:value-of select="string-length(substring-before(preceding-sibling::text()[1], '
'))"/> </xsl:with-param> </xsl:call-template> </pre> </xsl:template> <!-- 递归处理每一行,移除多余制表符 --> <xsl:template name="clean-code-content"> <xsl:param name="raw-text"/> <xsl:param name="indent-num"/> <xsl:param name="line-break" select="'
'"/> <!-- 拆分出当前第一行 --> <xsl:variable name="current-line" select="substring-before(concat($raw-text, $line-break), $line-break)"/> <!-- 剩下的内容 --> <xsl:variable name="rest-text" select="substring-after($raw-text, $line-break)"/> <!-- 去掉当前行开头的n个制表符,输出结果 --> <xsl:value-of select="substring($current-line, $indent-num + 1)"/> <!-- 还有内容就继续递归处理下一行 --> <xsl:if test="$rest-text"> <xsl:value-of select="$line-break"/> <xsl:call-template name="clean-code-content"> <xsl:with-param name="raw-text" select="$rest-text"/> <xsl:with-param name="indent-num" select="$indent-num"/> </xsl:call-template> </xsl:if> </xsl:template> <!-- 默认模板:保留其他节点原样输出 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
关键部分说明
- 计算缩进数:通过
preceding-sibling::text()[1]拿到<code>标签前面的文本节点,统计换行前的字符长度(也就是制表符的数量)。 - 预处理文本:
normalize-space(., ' \t')会保留换行符,同时把内容首尾的空白(包括换行)清掉,避免输出多余空行。 - 逐行清理:用递归的方式把文本拆成一行一行,每一行都从第n+1个字符开始截取,相当于去掉开头n个制表符。
- 兼容其他内容:最后的默认模板保证XML里其他标签和内容不受影响,正常输出。
测试效果
用你提供的XML示例跑这个XSLT,输出的<pre>内容会和预期完全一致——XML缩进带来的多余制表符被去掉了,代码本身的缩进格式还保留着。
内容的提问来源于stack exchange,提问作者fauve
相关产品推荐
相关产品推荐

