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

如何在生成的XSLT文件中创建可用于比较的字符串序列

问题

我正在编写XSLT文件「X_start」,用于从包含规则的XML文件生成另一XSLT文件「X_generated」。需求是在X_generated中创建字符串序列,将XML读取的值与规则定义的允许值比较,不匹配则生成提示文本。目前所有校验尝试均失败。

属性值"conditional"表示:模板匹配对应节点时,节点值必须符合指定的允许值集合。

规则文件示例:

<?xml version="1.0" encoding="UTF-8"?>
<rules>
    <rule type="conditional">
        <xPath>//elemA/@attribute1</xPath>
        <val allow="A"/>
        <val allow="B"/>
    </rule>
    <rule type="conditional">
        <xPath>//elemC/@attribute1</xPath>
        <val allow="C"/>
        <val allow="D"/>
    </rule>
    <rule type="conditional">
        <xPath>//elemB</xPath>
        <val allow="one"/>
        <val allow="two"/>
        <val allow="three"/>
    </rule>
</rules>

我的X_start草稿:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias"
    version="3.0">
    <xsl:output method="xml" indent="true" encoding="UTF-8"/>
    <xsl:mode on-no-match="shallow-skip"/>
    <xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>
    
    <xsl:template match="/">
        <axsl:stylesheet>
            <xsl:attribute name="expand-text" select="'true'"/>
            <xsl:attribute name="version" select="'3.0'"/>
            <xsl:apply-templates select="rules/*"/>
        </axsl:stylesheet>
    </xsl:template>
    
    <xsl:template match="rule">
        
        <!-- This creates ("A", "B") -->
        <xsl:variable name="vals_from_for_each" as="xs:string*">
            <xsl:for-each select="val/@allow">
                <xsl:value-of select="."/>
            </xsl:for-each>
        </xsl:variable>
        
        <!-- This also creates ("A", "B") -->
        <xsl:variable name="vals_from_seq_text" as="xs:string*">
            <xsl:sequence select="val/@allow"/>
        </xsl:variable>
        
        
        <axsl:template>            
            <xsl:attribute name="match" select="xPath"/>
            
            <!-- Attempt to move the sequence into the generated template -->
            <axsl:variable name="vals" as="xs:string*">
                <!-- But this generates "A B", and is therefore not valid for comparison. 
                    I cannot compare a value like ". = A B", it needs to be ". = ('A', 'B')-->
                <xsl:sequence select="$vals_from_seq_text"/> 
            </axsl:variable>
            <axsl:if test=". = $vals">
                <axsl:message>Success</axsl:message>
            </axsl:if>
            
            <!-- trying by building a comparison tree (that doesn't work either... -->
            <axsl:variable name="value_list" as="element()*">
                <root>
                    <xsl:for-each select="val/@allow">
                        <val><xsl:value-of select="."/></val>
                    </xsl:for-each>
                </root>
            </axsl:variable>
            
            <axsl:if test=". = $value_list/root/val">
                <axsl:message>Found in tree - Success</axsl:message>
            </axsl:if>
        </axsl:template>
    </xsl:template>
</xsl:stylesheet>

生成的X_generated:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    expand-text="true"
    version="3.0">
    <xsl:template match="//elemA/@attribute1">
        <xsl:variable name="vals" as="xs:string*">A B</xsl:variable>
        <xsl:if test=". = $vals">
            <xsl:message>Success</xsl:message>
        </xsl:if>
        <xsl:variable name="value_list" as="element()*">
            <root>
                <val>A</val>
                <val>B</val>
            </root>
        </xsl:variable>
        <xsl:if test=". = $value_list/root/val">
            <xsl:message>Found in tree - Success</xsl:message>
        </xsl:if>
    </xsl:template>
    <xsl:template match="//elemC/@attribute1">
        <xsl:variable name="vals" as="xs:string*">C D</xsl:variable>
        <xsl:if test=". = $vals">
            <xsl:message>Success</xsl:message>
        </xsl:if>
        <xsl:variable name="value_list" as="element()*">
            <root>
                <val>C</val>
                <val>D</val>
            </root>
        </xsl:variable>
        <xsl:if test=". = $value_list/root/val">
            <xsl:message>Found in tree - Success</xsl:message>
        </xsl:if>
    </xsl:template>
    <xsl:template match="//elemB">
        <xsl:variable name="vals" as="xs:string*">one two three</xsl:variable>
        <xsl:if test=". = $vals">
            <xsl:message>Success</xsl:message>
        </xsl:if>
        <xsl:variable name="value_list" as="element()*">
            <root>
                <val>one</val>
                <val>two</val>
                <val>three</val>
            </root>
        </xsl:variable>
        <xsl:if test=". = $value_list/root/val">
            <xsl:message>Found in tree - Success</xsl:message>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

目前所有校验提示均未触发,生成的序列被转换为空格分隔的字符串,无法有效比较;构建的树形结构也无法用于校验。曾考虑使用单独模式生成独立的XML查找文档,但希望找到更优方案,将XML规则中的允许值正确转换为X_generated中可用于比较的序列。


解决方案

问题核心是直接输出序列值时,XSLT会默认将序列拼接为空格分隔的字符串,而非生成XPath可识别的序列语法。要解决这个问题,需要在生成的XSLT中直接构造('值1', '值2', ...)格式的序列字面量。

以下是优化后的X_start代码:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    xmlns:axsl="http://www.w3.org/1999/XSL/TransformAlias"
    version="3.0">
    <xsl:output method="xml" indent="true" encoding="UTF-8"/>
    <xsl:mode on-no-match="shallow-skip"/>
    <xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>
    
    <xsl:template match="/">
        <axsl:stylesheet>
            <xsl:attribute name="expand-text" select="'true'"/>
            <xsl:attribute name="version" select="'3.0'"/>
            <xsl:apply-templates select="rules/*"/>
        </axsl:stylesheet>
    </xsl:template>
    
    <xsl:template match="rule">
        <!-- 构造带引号的允许值序列字符串,格式为 "'A', 'B'" -->
        <xsl:variable name="quoted-vals" as="xs:string">
            <xsl:value-of select="val/@allow ! concat('''', ., '''')" separator=", "/>
        </xsl:variable>
        
        <axsl:template>            
            <xsl:attribute name="match" select="xPath"/>
            
            <!-- 直接生成序列字面量 -->
            <axsl:variable name="vals" as="xs:string*" select="({$quoted-vals})"/>
            
            <!-- 校验逻辑:值不在序列中则提示错误 -->
            <axsl:if test=". not in $vals">
                <axsl:message terminate="no">错误:值「{.}」不在允许列表中,允许值为:{$quoted-vals}</axsl:message>
            </axsl:if>
            
            <!-- 可选:保留成功提示 -->
            <axsl:if test=". = $vals">
                <axsl:message>Success: 值「{.}」符合规则</axsl:message>
            </axsl:if>
        </axsl:template>
    </xsl:template>
</xsl:stylesheet>

关键修改说明

  1. 构造带引号的允许值:使用!(简单映射运算符)给每个允许值添加单引号,并用逗号分隔,生成类似'A', 'B'的字符串。
  2. 生成序列字面量:在axsl:variable的select属性中直接使用({$quoted-vals}),生成的XSLT中会出现select="('A', 'B')",直接创建字符串序列。
  3. 优化校验逻辑:使用not in运算符更直观地判断值是否不在允许列表中,同时输出清晰的错误提示。

生成的X_generated示例片段

<xsl:template match="//elemA/@attribute1">
    <xsl:variable name="vals" as="xs:string*" select="('A', 'B')"/>
    <xsl:if test=". not in $vals">
        <xsl:message terminate="no">错误:值「{.}」不在允许列表中,允许值为:'A', 'B'</xsl:message>
    </xsl:if>
    <xsl:if test=". = $vals">
        <xsl:message>Success: 值「{.}」符合规则</xsl:message>
    </xsl:if>
</xsl:template>

效果验证

当处理目标XML时,若节点值为A,会输出成功提示;若值为X,则会输出错误提示,明确告知允许的取值范围。

内容的提问来源于stack exchange,提问作者Zug_Bug

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 00:05:28