Xml 如何测试XSLT中空格分隔值的所有排列?
在测试Xml 如何测试XSLT中空格分隔值的所有排列?,xml,xslt,permutation,Xml,Xslt,Permutation,在测试xsl:when中变量的可能值(用空格分隔)时,我需要合并所有排列 例如: <xsl:when test="$var='A B C' or $var='B A C' or $var='...' or ...> <xsl:value-of select="X+Z"/> 我将测试源的所有值是否都存在于目标中,并且源和目标都包含相同数量的值,而不是尝试生成所有排列 这在XSLT
xsl:when
中变量的可能值(用空格分隔)时,我需要合并所有排列
例如:
<xsl:when test="$var='A B C'
or $var='B A C'
or $var='...'
or ...>
<xsl:value-of select="X+Z"/>
我将测试源的所有值是否都存在于目标中,并且源和目标都包含相同数量的值,而不是尝试生成所有排列
这在XSLT 1.0中有点冗长,但仍然:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:param name="delimiter" select="' '"/>
<xsl:variable name="source" select="'A B C'"/>
<xsl:variable name="target" select="'B A C'"/>
<xsl:variable name="every-source-in-target">
<xsl:call-template name="every-source-in-target">
<xsl:with-param name="source" select="$source"/>
<xsl:with-param name="target" select="$target"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="count-source" select="string-length(translate($source, translate($source, $delimiter, ''), ''))" />
<xsl:variable name="count-target" select="string-length(translate($target, translate($target, $delimiter, ''), ''))" />
<xsl:template match="/">
<result>
<xsl:if test="$every-source-in-target='true' and $count-source=$count-target ">MATCH</xsl:if>
</result>
</xsl:template>
<xsl:template name="every-source-in-target">
<xsl:param name="source"/>
<xsl:param name="target"/>
<xsl:param name="delimiter" select="' '"/>
<xsl:variable name="token" select="substring-before(concat($source, $delimiter), $delimiter)" />
<xsl:choose>
<xsl:when test="not(contains(concat($delimiter, $target, $delimiter), concat($delimiter, $token, $delimiter)))">
<xsl:value-of select="false()"/>
</xsl:when>
<xsl:when test="contains($source, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="every-source-in-target">
<xsl:with-param name="source" select="substring-after($source, $delimiter)"/>
<xsl:with-param name="target" select="$target"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="true()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
下面是一个简单的XPath 1.0解决方案:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:variable name="x" select="'B A C'"/>
<xsl:variable name="y" select="'A B C'"/>
<xsl:template match="/">
<xsl:choose>
<xsl:when test="string-length($x) = string-length($y)
and translate($x, $y, '') = ''">
<xsl:message>Same</xsl:message>
</xsl:when>
<xsl:otherwise>
<xsl:message>Different</xsl:message>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
同样的
不同的
注意,这假设唯一的单字符值被置换
验证单个字符串是否为精确字符串的有效解决方案
给定字符串集的排列如下所示:
下面验证源XML文档顶部元素的子元素是否都是给定字符串集的可能排列
规则:
必须使用单个空格作为分隔符
给定字符串集中的任何字符串都不包含空格
XML文档顶部元素的任何子元素的值都必须是规范化的字符串——应该只包含内部单个空格,每个空格分隔两个相邻的非空格子字符串
如果/*/*
(XML文档顶部元素的子元素)的字符串值表示给定字符串集项的所有可能排列,并且只表示一次,则转换将生成字符串“Valid input”
如果不是这样,转换将终止,并显示诊断消息,解释所发现的确切冲突
字符串集的项表示为XML元素的子元素,该元素是名为prtfData的全局参数的值
处理将随此消息终止:
<t>
<v>C B A</v>
<v> C A B </v>
<v>B A C</v>
<v>B C A</v>
<v>A C B</v>
<v>A B C</v>
</t>
<t>
<v>C B A</v>
<v>C C B</v>
<v>B A C</v>
<v>B C A</v>
<v>A C B</v>
<v>A B C</v>
</t>
<t>
<v>C B A</v>
<v>C C B</v>
<v>B A C</v>
</t>
<t>
<v>C B A</v>
<v>C A B</v>
<v>C A B</v>
<v>B A C</v>
<v>A C B</v>
<v>A B C</v>
</t>
<t>
<!-- Correct -->
<v>A B C</v>
<v>A C B</v>
<v>B A C</v>
<v>B C A</v>
<v>C A B</v>
<v>C B A</v>
<!-- Incorrect -->
<v>C A C</v>
<v>A B</v>
<v></v>
<v>A B C D</v>
</t>
错误:某些数据项相等。并不是所有的排列都有代表性
如果所有字符串都是有效名称,那么一个简洁的2.0解决方案是将字符串转换为属性并使用deep-equals()
deep-equal(f:to-atts(源),f:to-atts(目标))
注意:这消除了重复:“A”将等于“A”。您还没有说这是否可取。快速简便的方法是首先定义一个变量,将分隔符放在开头和结尾,如下所示:
<xsl:variable name="tempVar" select="concat(' ',$var,' ')"/>
然后简单地使用
<xsl:when test="contains($tempVar,' A ') and
contains($tempVar, ' B ') and
contains($tempVar, ' C ')">
<xsl:value-of select="X+Z"/>
</xsl:when>
在tempVar
的开始和结束处放置一个空格只意味着所有值都被空格包围,无论是否在开始/结束处,这样就可以只检查每一个值是否存在,每边都有一个空格
当然,您可以不使用该变量,只需在test
属性中重复concat表达式三次。一个XSLT2.0解决方案,它可以避免排序,而且效率更高:O(N)
vs.O(N*log(N))
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:my="my:my">
<xsl:output method="text"/>
<xsl:key name="kAllItems" match="x" use="."/>
<xsl:param name="pData">
<x>A</x>
<x>B</x>
<x>C</x>
</xsl:param>
<xsl:template match="v">
(<xsl:value-of select="."/>) ==> <xsl:sequence
select="my:exactPremutation(., $pData)"/>
</xsl:template>
<xsl:function name="my:exactPremutation" as="xs:boolean">
<xsl:param name="pInput" as="xs:string"/>
<xsl:param name="pDataItems" as="document-node()"/>
<xsl:variable name="vNumDataItems" select="count($pDataItems/*)"/>
<xsl:variable name="vMatches" as="xs:integer*">
<xsl:for-each-group select="tokenize($pInput, '\s+')" group-by=".">
<xsl:variable name="vMatchedDataItem"
select="key('kAllItems', current-grouping-key(), $pDataItems)"/>
<xsl:sequence select="1[not(current-group()[2]) and $vMatchedDataItem]"/>
<xsl:sequence select="($vNumDataItems +1)[not($vMatchedDataItem)]"/>
</xsl:for-each-group>
</xsl:variable>
<xsl:sequence select="sum($vMatches) eq $vNumDataItems"/>
</xsl:function>
</xsl:stylesheet>
注意:线性效率评估假设XSLT处理器使用高效(如基于哈希表的)分组实现。您可以使用XSLT 2.0吗?我更喜欢XSLT 1.0,如果不可能,我可以探索2.0选项。谢谢。这个解决方案似乎很贵。当然,对两个序列进行排序并逐项比较结果的解决方案会更有效率吗?另外,我认为不应该鼓励使用文档(“”
)。它假定在一个解释环境中,样式表的源代码在运行时可用,但情况并非总是如此。@MichaelKay,如果我们必须验证n!字符串,这将始终至少是O(n!)——即使使用了哈希表。通过对值进行排序并使用二进制搜索,我们需要O(n!*log(n!)来进行排序,然后使用二进制搜索将得到O(log(n!))。因此,我完全同意任何此类验证显然都是低效的。可能我们对OP想要什么有不同的理解。我的理解是,他想要验证一组字符串是否完全包含预定义字符串集的所有置换(每个字符串表示一个置换)。我对这个问题的解释是“我想测试$var是否在某种排列中包含标记“abc”。也就是说,deepequal(tokenize($var)=>sort(),tokenize(“abc”)=>sort())
@MichaelKay,是的,对于这种解释,这是一种合适的方式。我认为使用键(例如tokenize($var)中的每$t)满足key('kn',$var,$stylesheetDoc)
可以是O(N),而排序解决方案是O(N*log(N))。在这里,我们将“A”、“B”和“C”作为树中节点的字符串值——甚至可以将此树作为参数传递。@MichaelKay,我看到我在前面的评论中提出的建议不会讨厌字符串的重复。我创建了另一个答案,它使用所有排列作为键值。这在不超过7-8的情况下是实用的不同的字符串,可以方便地用XSLT 1.0表示
<xsl:variable name="tempVar" select="concat(' ',$var,' ')"/>
<xsl:when test="contains($tempVar,' A ') and
contains($tempVar, ' B ') and
contains($tempVar, ' C ')">
<xsl:value-of select="X+Z"/>
</xsl:when>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:my="my:my">
<xsl:output method="text"/>
<xsl:key name="kAllItems" match="x" use="."/>
<xsl:param name="pData">
<x>A</x>
<x>B</x>
<x>C</x>
</xsl:param>
<xsl:template match="v">
(<xsl:value-of select="."/>) ==> <xsl:sequence
select="my:exactPremutation(., $pData)"/>
</xsl:template>
<xsl:function name="my:exactPremutation" as="xs:boolean">
<xsl:param name="pInput" as="xs:string"/>
<xsl:param name="pDataItems" as="document-node()"/>
<xsl:variable name="vNumDataItems" select="count($pDataItems/*)"/>
<xsl:variable name="vMatches" as="xs:integer*">
<xsl:for-each-group select="tokenize($pInput, '\s+')" group-by=".">
<xsl:variable name="vMatchedDataItem"
select="key('kAllItems', current-grouping-key(), $pDataItems)"/>
<xsl:sequence select="1[not(current-group()[2]) and $vMatchedDataItem]"/>
<xsl:sequence select="($vNumDataItems +1)[not($vMatchedDataItem)]"/>
</xsl:for-each-group>
</xsl:variable>
<xsl:sequence select="sum($vMatches) eq $vNumDataItems"/>
</xsl:function>
</xsl:stylesheet>
<t>
<!-- Correct -->
<v>A B C</v>
<v>A C B</v>
<v>B A C</v>
<v>B C A</v>
<v>C A B</v>
<v>C B A</v>
<!-- Incorrect -->
<v>C A C</v>
<v>A B</v>
<v></v>
<v>A B C D</v>
</t>
(A B C) ==> true
(A C B) ==> true
(B A C) ==> true
(B C A) ==> true
(C A B) ==> true
(C B A) ==> true
(C A C) ==> false
(A B) ==> false
() ==> false
(A B C D) ==> false