You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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, &lt;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, &lt;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, &lt;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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 19:50:54