XML缩进移除后空格插入异常的XSL技术求助
问题
移除XML文档缩进时遇到困难,尝试了两种方法:
- 添加
<xsl:strip-space elements="*"/> - 对所有文本应用
normalize-space
发现必须同时使用二者才能完全移除缩进,但normalize-space会过度删除空格。于是编写了以下XSL代码,试图为除段落开头外的所有文本段前添加空格,却发现部分文本(如示例中的"third text2")前未插入空格,这类问题在文档中普遍存在,怀疑<xsl:strip-space>配置与空格插入存在冲突,请问是什么原因导致无法插入所需空格?
相关代码
XSL 代码
<xsl:template match="p"> <xsl:variable name="first_text" select="descendant::text()[normalize-space(.) != ''][1]"/> <xsl:copy copy-namespaces="no"> <xsl:copy-of select="@*"/> <xsl:apply-templates select="element()|text()" mode="spacing"> <xsl:with-param name="first_text" select="$first_text" tunnel="yes"/> </xsl:apply-templates> </xsl:copy> </xsl:template> <xsl:template match="*" mode="spacing"> <xsl:copy copy-namespaces="no"> <xsl:copy-of select="@*"/> <xsl:apply-templates mode="spacing"/> </xsl:copy> </xsl:template> <xsl:template match="text()" mode="spacing"> <xsl:param name="first_text" tunnel="yes"/> <xsl:choose> <xsl:when test="$first_text = ."> <xsl:value-of select="normalize-space(.)"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="concat('&#32;', normalize-space(.))"/> </xsl:otherwise> </xsl:choose> </xsl:template>
XML 输入
<topic class="- topic/topic " id="topic_1"> <title class="- topic/title ">Title</title> <body> <p class="- topic/p "><i class="+ topic/ph hi-d/i ">First text1<xref class="- topic/xref "><b class="+ topic/ph hi-d/b ">second text1</b></xref>third text1.</i> </p> <p class="- topic/p "><i class="+ topic/ph hi-d/i "><b class="+ topic/ph hi-d/b ">First text2<keyword class="- topic/keyword ">second text2</keyword></b>third text2.</i> </p> </body> </topic>
XML 输出(为便于查看已添加缩进)
<topic class="- topic/topic " id="topic_1"> <title class="- topic/title ">Title</title> <body class="- topic/body "> <p class="- topic/p "><i class="+ topic/ph hi-d/i ">First text1<xref class="- topic/xref "><b class="+ topic/ph hi-d/b "> second text1</b></xref> third text1. </i> </p> <p class="- topic/p "><i class="+ topic/ph hi-d/i "><b class="+ topic/ph hi-d/b ">First text2<keyword class="- topic/keyword "> second text2</keyword></b>third text2.</i> </p> </body>
原因分析与解决方案
核心问题
空格未正确插入的问题,与<xsl:strip-space>无关,主要源于两个代码逻辑错误:
1. 节点判断逻辑错误
原代码用$first_text = .判断当前文本节点是否为段落首文本节点,这是字符串值比较,而非节点身份匹配。这种方式存在两个问题:
- 若文档中出现与首文本节点内容完全相同的其他文本,会错误跳过空格添加;
- 极端情况下,文本节点的字符串值可能因空白处理出现意外相等,导致逻辑失效。
2. 空格生成方式错误
原代码用concat('&#32;', normalize-space(.))生成空格,&#32;会被输出为XML实体 ,而非实际空格字符。除非XML被解析渲染,否则原始输出中看不到空格,看起来像是未插入空格。
修正方案
步骤1:修正节点身份判断
使用generate-id()函数比较节点唯一标识,确保只有真正的首文本节点跳过空格:
<xsl:when test="generate-id($first_text) = generate-id(.)">
步骤2:直接使用空格字符
将XML实体替换为实际空格字符,确保输出可见的空格:
<xsl:value-of select="concat(' ', normalize-space(.))"/>
完整修正后的XSL代码
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:strip-space elements="*"/> <xsl:template match="p"> <xsl:variable name="first_text" select="descendant::text()[normalize-space(.) != ''][1]"/> <xsl:copy copy-namespaces="no"> <xsl:copy-of select="@*"/> <xsl:apply-templates select="element()|text()" mode="spacing"> <xsl:with-param name="first_text" select="$first_text" tunnel="yes"/> </xsl:apply-templates> </xsl:copy> </xsl:template> <xsl:template match="*" mode="spacing"> <xsl:copy copy-namespaces="no"> <xsl:copy-of select="@*"/> <xsl:apply-templates mode="spacing"/> </xsl:copy> </xsl:template> <xsl:template match="text()" mode="spacing"> <xsl:param name="first_text" tunnel="yes"/> <xsl:choose> <xsl:when test="generate-id($first_text) = generate-id(.)"> <xsl:value-of select="normalize-space(.)"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="concat(' ', normalize-space(.))"/> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
效果验证
修正后,所有非首文本节点前都会正确插入空格,同时<xsl:strip-space>仍能有效移除原始XML的缩进空白,normalize-space也不会破坏必要的文本间距。
内容的提问来源于stack exchange,提问作者jcalder1969
相关产品推荐
相关产品推荐

