XSLT代码重复变量名检测与自动替换方案咨询
问题
用自动代码生成工具生成XSLT代码时,出现变量名重复声明的问题,导致运行异常。比如下面的代码中,var7_map_of_House被重复声明了两次:
<xsl:template name="My_Template"> <xsl:variable name="var7_map_of_House" as="xs:string*"> <xsl:for-each select="$var9_House/Item"> <xsl:sequence select="fn:string(.)"/> </xsl:for-each> </xsl:variable> <xsl:variable name="var8_count_as_string" as="xs:string" select="xs:string(fn:count($var7_map_of_House))"/> <xsl:variable name="var5_map_of_HCon" as="xs:string*"> <xsl:for-each select="$var9_House/Item/postcode"> <xsl:sequence select="fn:string(.)"/> </xsl:for-each> </xsl:variable> <xsl:variable name="var7_map_of_House" as="xs:string" select="xs:string(fn:count($var5_map_of_HCon))"/> <xsl:variable name="var1_map_of_HouseCon" as="xs:string*"> <xsl:for-each select="$var9_House/Item/name"> <xsl:sequence select="fn:string(.)"/> </xsl:for-each> </xsl:variable> </xsl:template>
需要识别重复的变量名,将其替换为唯一名称(比如var27_map_of_House),同时更新该变量声明后的所有引用,目标代码如下:
<xsl:template name="My_Template"> <xsl:variable name="var7_map_of_House" as="xs:string*"> <xsl:for-each select="$var9_House/Item"> <xsl:sequence select="fn:string(.)"/> </xsl:for-each> </xsl:variable> <xsl:variable name="var8_count_as_string" as="xs:string" select="xs:string(fn:count($var7_map_of_House))"/> <xsl:variable name="var5_map_of_HCon" as="xs:string*"> <xsl:for-each select="$var9_House/Item/postcode"> <xsl:sequence select="fn:string(.)"/> </xsl:for-each> </xsl:variable> <xsl:variable name="var27_map_of_House" as="xs:string" select="xs:string(fn:count($var5_map_of_HCon))"/> <xsl:variable name="var1_map_of_HouseCon" as="xs:string*"> <xsl:for-each select="$var9_House/Item/name"> <xsl:sequence select="fn:string(.)"/> </xsl:for-each> </xsl:variable> </xsl:template>
解决方案
这个需求完全可以实现,以下是具体实现方法:
一、XSLT实现方案(推荐)
我们可以用XSLT 2.0+来处理XSLT代码(将XSLT作为XML文档解析),核心思路是:
- 跟踪每个变量名的声明次数
- 遇到重复声明时,生成新的唯一变量名
- 自动替换该声明后的所有变量引用
实现代码
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xsl:output method="xml" indent="yes" encoding="UTF-8"/> <!-- 基础复制模板,传递变量计数参数 --> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"> <xsl:with-param name="var-counts" select="()" tunnel="yes"/> </xsl:apply-templates> </xsl:copy> </xsl:template> <!-- 处理xsl:variable元素 --> <xsl:template match="xsl:variable"> <xsl:param name="var-counts" tunnel="yes"/> <xsl:variable name="current-name" select="@name"/> <xsl:variable name="existing-count" select="count($var-counts[@name = $current-name])"/> <xsl:choose> <!-- 首次声明,保留原名并更新计数 --> <xsl:when test="$existing-count = 0"> <xsl:copy> <xsl:apply-templates select="@*|node()"> <xsl:with-param name="var-counts" select="$var-counts, <entry name='{$current-name}' count='1'/>" tunnel="yes"/> </xsl:apply-templates> </xsl:copy> </xsl:when> <!-- 重复声明,生成新名称并更新引用映射 --> <xsl:otherwise> <!-- 生成新变量名:原数字部分加20,可自定义规则 --> <xsl:variable name="new-name"> <xsl:analyze-string select="$current-name" regex="var(\d+)_(.*)"> <xsl:matching-substring> <xsl:value-of select="concat('var', number(regex-group(1)) + 20, '_', regex-group(2))"/> </xsl:matching-substring> <xsl:non-matching-substring> <xsl:value-of select="concat($current-name, '_', $existing-count)"/> </xsl:non-matching-substring> </xsl:analyze-string> </xsl:variable> <!-- 替换变量名并更新计数 --> <xsl:copy> <xsl:attribute name="name" select="$new-name"/> <xsl:apply-templates select="@*[local-name() != 'name']|node()"> <xsl:with-param name="var-counts" select="$var-counts, <entry name='{$new-name}' count='1'/>" tunnel="yes"/> </xsl:apply-templates> </xsl:copy> <!-- 传递重命名映射,用于后续替换引用 --> <xsl:next-match> <xsl:with-param name="var-counts" select="$var-counts, <entry name='{$current-name}' new-name='{$new-name}' count='{$existing-count + 1}'/>" tunnel="yes"/> </xsl:next-match> </xsl:otherwise> </xsl:choose> </xsl:template> <!-- 处理属性中的变量引用(如select属性) --> <xsl:template match="@*"> <xsl:param name="var-counts" tunnel="yes"/> <xsl:variable name="new-value"> <xsl:analyze-string select="." regex="\$([a-zA-Z0-9_]+)"> <xsl:matching-substring> <xsl:variable name="var-name" select="regex-group(1)"/> <xsl:variable name="new-var-name" select="$var-counts[@name = $var-name]/@new-name"/> <xsl:choose> <xsl:when test="$new-var-name"> <xsl:value-of select="concat('$', $new-var-name)"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="."/> </xsl:otherwise> </xsl:choose> </xsl:matching-substring> <xsl:non-matching-substring> <xsl:value-of select="."/> </xsl:non-matching-substring> </xsl:analyze-string> </xsl:variable> <xsl:attribute name="{local-name()}" select="$new-value"/> </xsl:template> </xsl:stylesheet>
说明
- 支持XSLT 2.0及以上版本(依赖隧道参数和正则分析功能)
- 重复变量名的生成规则可自定义(比如改为递增数字、随机数)
- 自动替换所有
$变量名形式的引用,无需手动修改
二、Python实现方案
如果需要用Python处理,可以借助lxml库解析XML,遍历节点跟踪变量名并替换重复项:
实现代码
import re from lxml import etree def fix_duplicate_variables(xslt_content): parser = etree.XMLParser(remove_blank_text=True) root = etree.fromstring(xslt_content, parser) var_counts = {} var_renames = {} # 遍历所有xsl:variable元素,处理重复声明 for var in root.xpath("//xsl:variable", namespaces={"xsl": "http://www.w3.org/1999/XSL/Transform"}): var_name = var.get("name") if var_name not in var_counts: var_counts[var_name] = 1 else: # 生成新变量名 match = re.match(r"var(\d+)_(.*)", var_name) if match: num = int(match.group(1)) + 20 new_name = f"var{num}_{match.group(2)}" else: new_name = f"{var_name}_{var_counts[var_name]}" # 记录重命名映射 var_renames[var_name] = new_name # 更新变量名 var.set("name", new_name) var_counts[var_name] += 1 # 替换所有属性中的变量引用 for attr in root.xpath("//@*"): value = attr.value for old_name, new_name in var_renames.items(): value = re.sub(rf"\${old_name}\b", f"${new_name}", value) attr.value = value return etree.tostring(root, pretty_print=True, encoding="utf-8").decode() # 使用示例 if __name__ == "__main__": with open("problematic.xslt", "r", encoding="utf-8") as f: xslt_content = f.read() fixed_content = fix_duplicate_variables(xslt_content) with open("fixed.xslt", "w", encoding="utf-8") as f: f.write(fixed_content)
说明
- 依赖
lxml库,可通过pip install lxml安装 - 逻辑简单直观,适合快速处理小型XSLT文件
- 变量名生成规则可根据需求调整
内容的提问来源于stack exchange,提问作者RamAlx
相关产品推荐
相关产品推荐

