如何在XSLT中定位闭合标签前的特定字符串并为元素添加动态属性
没问题,这个需求在XSLT里完全可以实现,核心就是精准定位标签内容末尾的括号内数字串,而不是抓所有符合规则的匹配项。我给你拆解具体实现思路和代码:
核心思路
我们需要两步走:
- 找到
div下的title或p元素,判断其文本内容是否以括号包裹的目标数字串结尾; - 提取符合要求的串(数字总位数为2/4/6/10,允许带横杠分隔),作为
div的id属性。
XSLT 2.0+ 解决方案(推荐)
XSLT 2.0及以上支持正则相关函数,实现起来更简洁:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 匹配div元素,添加id属性 --> <xsl:template match="div"> <!-- 处理title元素的候选id --> <xsl:variable name="title-content" select="normalize-space(title/text())"/> <xsl:variable name="title-candidate" select="replace($title-content, '^.*\(([^\)]+)\)$', '$1')"/> <xsl:variable name="title-id" select="$title-candidate[ string-length(translate(., '-', '')) = (2,4,6,10) and $title-candidate != $title-content ]"/> <!-- 处理p元素的候选id --> <xsl:variable name="p-content" select="normalize-space(p/text())"/> <xsl:variable name="p-candidate" select="replace($p-content, '^.*\(([^\)]+)\)$', '$1')"/> <xsl:variable name="p-id" select="$p-candidate[ string-length(translate(., '-', '')) = (2,4,6,10) and $p-candidate != $p-content ]"/> <!-- 确定最终id:优先用title的匹配结果,没有则用p的 --> <xsl:variable name="final-id" select="$title-id | $p-id"/> <!-- 生成带id的div --> <div> <xsl:if test="$final-id"> <xsl:attribute name="id" select="$final-id"/> </xsl:if> <!-- 复制原div的所有子内容 --> <xsl:apply-templates select="@* | node()"/> </div> </xsl:template> <!-- 默认模板:复制所有其他节点和属性,保证原XML结构不变 --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
代码解释
- 正则匹配末尾括号:
replace($content, '^.*\(([^\)]+)\)$', '$1')只会提取字符串最后一对括号里的内容,中间出现的括号串会被忽略; - 验证数字位数:
translate(., '-', '')去掉串里的横杠,再用string-length统计数字总长度,确保是2/4/6/10位; - 优先级处理:优先从
title提取,没有匹配结果再用p的,符合你给出的两种场景逻辑; - 默认模板:保证原XML的其他元素、属性完全保留,只修改
div的id。
适配XSLT 1.0的方案
如果只能用XSLT 1.0(没有replace函数),可以用字符串截取的方式实现:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 匹配div元素 --> <xsl:template match="div"> <xsl:variable name="title-id"> <xsl:call-template name="extract-valid-id"> <xsl:with-param name="text" select="normalize-space(title/text())"/> </xsl:call-template> </xsl:variable> <xsl:variable name="p-id"> <xsl:call-template name="extract-valid-id"> <xsl:with-param name="text" select="normalize-space(p/text())"/> </xsl:call-template> </xsl:variable> <xsl:variable name="final-id" select="concat($title-id, $p-id)"/> <div> <xsl:if test="$final-id != ''"> <xsl:attribute name="id"><xsl:value-of select="$final-id"/></xsl:attribute> </xsl:if> <xsl:apply-templates select="@* | node()"/> </div> </xsl:template> <!-- 提取末尾括号内的有效id --> <xsl:template name="extract-valid-id"> <xsl:param name="text"/> <xsl:if test="contains($text, '(') and substring($text, string-length($text)) = ')'"> <xsl:variable name="open-pos" select="string-length(substring-before($text, ')'))"/> <xsl:if test="substring($text, $open-pos, 1) = '('"> <xsl:variable name="candidate" select="substring($text, $open-pos + 1, string-length($text) - $open-pos - 1)"/> <xsl:variable name="digit-count" select="string-length(translate($candidate, '-', ''))"/> <xsl:if test="$digit-count = 2 or $digit-count = 4 or $digit-count = 6 or $digit-count = 10"> <xsl:value-of select="$candidate"/> </xsl:if> </xsl:if> </xsl:if> </xsl:template> <!-- 默认复制模板 --> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet>
这个方案用substring-before、substring等XSLT 1.0原生函数实现了相同的逻辑,只是代码稍长一些。
测试你的场景
- 情况1:
p文本末尾的(0123-45-6789)会被提取,数字总长度10,符合要求,最终div的id为0123-45-6789; - 情况2:
title文本末尾的(0123-45)会被提取,数字总长度6,符合要求,最终div的id为0123-45,和你期望的输出完全一致。
内容的提问来源于stack exchange,提问作者Takenaka
相关产品推荐
相关产品推荐

