使用XSL包裹XML中连续<b>元素的优化方案及空格问题咨询
<b>元素分组的XSLT方案 + 多余空格解决技巧 你提到的连续<b>元素分组问题,用XSLT 2.0及以上的xsl:for-each-group(group-adjacent特性)是目前最简洁高效的实现方式,比递归遍历或者兄弟节点判断要优雅得多。另外针对转换后出现的多余空格,也有几个针对性的处理方法,下面具体拆解:
一、更优的连续<b>分组实现
核心思路是利用group-adjacent属性,将相邻的<b>元素归为同一组,然后给每组套上你需要的容器标签(比如<bold-group>,可根据需求自定义)。非<b>元素则直接原样输出。
假设你的目标是把连续<b>包裹在<bold-group>中,示例XSLT代码如下:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 先关闭输出缩进,避免自动生成多余空格 --> <xsl:output method="xml" encoding="UTF-8" indent="no"/> <!-- 复制根节点<chap>,并处理其子节点 --> <xsl:template match="chap"> <xsl:copy> <xsl:apply-templates select="p"/> </xsl:copy> </xsl:template> <!-- 处理<p>节点,对其子节点按是否为<b>进行相邻分组 --> <xsl:template match="p"> <xsl:copy> <xsl:for-each-group select="node()" group-adjacent="name() = 'b'"> <!-- 处理<b>节点组:套上容器标签 --> <xsl:if test="current-grouping-key()"> <bold-group> <xsl:copy-of select="current-group()"/> </bold-group> </xsl:if> <!-- 处理非<b>节点:直接复制输出 --> <xsl:if test="not(current-grouping-key())"> <xsl:copy-of select="current-group()"/> </xsl:if> </xsl:for-each-group> </xsl:copy> </xsl:template> <!-- 默认模板:复制所有未匹配的节点和属性,保证其他内容不丢失 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
这个方案的优势:
- 代码简洁逻辑清晰,无需复杂的条件判断或递归
- 支持任意数量的连续
<b>,不管节点间是否有空白文本 - 兼容XSLT 2.0及以上版本(如果必须用XSLT 1.0,只能通过递归模板实现,但复杂度会高很多,优先推荐2.0+)
二、解决转换后多余空格的问题
多余空格通常来自两个源头:原XML中的空白文本节点,或是XSLT输出时的自动缩进。可以从以下几个方向处理:
1. 过滤原XML中的纯空白文本节点
添加一个模板,直接忽略只包含空白的文本节点(不会影响内容中的正常空格):
<xsl:template match="text()[normalize-space() = '']"/>
如果只想过滤特定元素下的空白(比如<p>内的节点间空白),可以更精准地匹配:
<xsl:template match="p/text()[normalize-space() = '']"/>
2. 调整输出设置
将<xsl:output>的indent设为no,避免XSLT处理器自动添加缩进空格:
<xsl:output method="xml" encoding="UTF-8" indent="no"/>
如果需要格式化输出又不想有多余空格,可以搭配xsl:strip-space指定要去除空格的元素:
<xsl:strip-space elements="p chap"/> <!-- 去除这些元素下的空白节点 -->
3. 处理组内的空白(可选)
如果原XML中连续<b>之间有空格(比如示例里<b>The</b> <b>Attorney</b>的中间空格),若不需要保留,可以在分组时过滤这些空白:
<xsl:if test="current-grouping-key()"> <bold-group> <xsl:for-each select="current-group()[self::b]"> <xsl:copy-of select="."/> </xsl:for-each> </bold-group> </xsl:if>
这样会只保留<b>元素,自动剔除组内的空白文本节点。
测试效果
用你给出的输入片段:
<chap> <p><b>The</b> <b>Attorney</b> General <b>(Fees)</b><b>a</b><b>b</b><b>c</b> Determination 2012 No 110 commenced on 1...</p> </chap>
经过上述XSLT转换后,会输出:
<chap><p><bold-group><b>The</b><b>Attorney</b></bold-group> General <bold-group><b>(Fees)</b><b>a</b><b>b</b><b>c</b></bold-group> Determination 2012 No 110 commenced on 1...</p></chap>
(若开启xsl:strip-space,会进一步清理节点间的冗余空白)
内容的提问来源于stack exchange,提问作者Gurusen Yadav

