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