如何在生成的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>
关键修改说明
- 构造带引号的允许值:使用
!(简单映射运算符)给每个允许值添加单引号,并用逗号分隔,生成类似'A', 'B'的字符串。 - 生成序列字面量:在
axsl:variable的select属性中直接使用({$quoted-vals}),生成的XSLT中会出现select="('A', 'B')",直接创建字符串序列。 - 优化校验逻辑:使用
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
相关产品推荐
相关产品推荐

