XSLT键只返回一次值

时间:2013-05-24 05:13:02

标签: xslt key

我想我在这里遗漏了一些明显的东西但是这里有。我有以下xml,我需要将匹配实例的KEY节点组合在一起。这由match属性指定,它可以包含多个项目编号。可以有任意数量的ITEM节点和任意数量的KEY节点。此外,ITEM节点的深度没有限制。并且,匹配的实例不必位于同一父级下。我也仅限于XSLT 1.0和Microsoft解析器。

<?xml version="1.0" encoding="utf-8" ?>
<ITEM number='1'>
  <ITEM number='2'>
    <ITEM number='3' match='5,11'>
      <KEY name='key1' value='x' />
      <KEY name='key2' value='y' />
      <KEY name='key3' value='z' />
      <ITEM number ='4' />
    </ITEM>
    <ITEM number='5' match='3,11'>
      <KEY name='key1' value='x' />
      <KEY name='key2' value='y' />
      <KEY name='key3' value='z' />
    </ITEM>
    <ITEM number='6' match='10'>
      <KEY name='key1' value='x' />
      <KEY name='key2' value='y' />
      <KEY name='key4' value='a' />
    </ITEM>
    <ITEM number='7' />
    <ITEM number='8'>
      <KEY name='key1' value='x' />
    </ITEM>
  </ITEM>
  <ITEM number='9'>
    <ITEM number='10' match='6'>
      <KEY name='key1' value='x' />
      <KEY name='key3' value='z' />
      <KEY name='key5' value='b' />
    </ITEM>
  </ITEM>
    <ITEM number='11' match='3,5'>
      <KEY name='key2' value='y' />
      <KEY name='key3' value='z' />
    </ITEM>
</ITEM>

我的预期结果看起来像这样......

<?xml version="1.0" encoding="utf-8" ?>
<Result>
  <Group number="1" />
  <Group number="2" />
  <Group number="3,5,11">
    <KEY name='key1' value='x' />
    <KEY name='key2' value='y' />
    <KEY name='key3' value='z' />
  </Group>
  <Group number="4" />
  <Group number="6,10">
    <KEY name='key1' value='x' />
    <KEY name='key2' value='y' />
    <KEY name='key3' value='z' />
    <KEY name='key4' value='a' />
    <KEY name='key5' value='b' />
  </Group>
  <Group number="7" />
  <Group number="8">
    <KEY name='key1' value='x' />
  </Group>
  <Group number="9" />
</Result>

我实际得到的是......

<?xml version="1.0" encoding="utf-8"?>
<Result>
  <Group number="1" />
  <Group number="2" />
  <Group number="3,5,11">
    <KEY name="key1" value="x" />
    <KEY name="key2" value="y" />
    <KEY name="key3" value="z" />
  </Group>
  <Group number="4" />
  <Group number="6,10">
    <KEY name="key4" value="a" />
    <KEY name="key5" value="b" />
  </Group>
  <Group number="7" />
  <Group number="8" />
  <Group number="9" />
</Result>

我正在使用密钥,看起来一旦我从密钥功能访问该特定值,我就无法再次访问它。组号6,10应包含所有5个键,但缺少前3个组,这些键已经存在于组号3,5中。类似地,对于组号8,它应该包含1个键。我已经使用递归来跳过匹配的实例,但我认为那里没有任何问题,它似乎与关键功能有关。我已将我的xslt附在下面,请看看我告诉我我做错了什么。任何提高性能的技巧也值得赞赏:)

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:output method="xml" indent="yes"/>

  <xsl:key name="kKeyByName" match="KEY" use="@name" />

  <xsl:template name="ProcessItem">
    <!--pItemsList - node set containing items that need to be processed-->
    <xsl:param name="pItemsList" />
    <!--pProcessedList - string containing processed item numbers in the format |1|2|3|-->
    <xsl:param name="pProcessedList" />

    <xsl:variable name="vCurrItem" select="$pItemsList[1]" />
    <!--Recursion exit condition - check if we have a valid Item-->
    <xsl:if test="$vCurrItem">
      <xsl:variable name="vNum" select="$vCurrItem/@number" />
      <!--Skip processed instances-->
      <xsl:if test="not(contains($pProcessedList, concat('|', $vNum, '|')))">
        <xsl:element name="Group">
          <!--If the item is matched with another item, only the distinct keys of the 2 should be displayed-->
          <xsl:choose>
            <xsl:when test="$vCurrItem/@match">
              <xsl:attribute name="number">
                <xsl:value-of select="concat($vNum, ',', $vCurrItem/@match)" />
              </xsl:attribute>
              <xsl:for-each select="(//ITEM[@number=$vNum or @match=$vNum]/KEY)[generate-id(.)=generate-id(key('kKeyByName', @name)[1])]">
                <xsl:apply-templates select="." />
              </xsl:for-each>
            </xsl:when>
            <xsl:otherwise>
              <xsl:attribute name="number">
                <xsl:value-of select="$vNum" />
              </xsl:attribute>
              <xsl:apply-templates select="KEY" />
            </xsl:otherwise>
          </xsl:choose>
        </xsl:element>
      </xsl:if>

      <!--Append processed instances to list to pass on in recursive function-->
      <xsl:variable name="vNewList">
        <xsl:value-of select="$pProcessedList" />
        <xsl:value-of select="concat($vNum, '|')" />
        <xsl:if test="$vCurrItem/@match">
          <xsl:value-of select="concat($vCurrItem/@match, '|')" />
        </xsl:if>
      </xsl:variable>

      <!--Call template recursively to process the rest of the instances-->
      <xsl:call-template name="ProcessItem">
        <xsl:with-param name="pItemsList" select="$pItemsList[position() > 1]" />
        <xsl:with-param name="pProcessedList" select="$vNewList" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>

  <xsl:template match="KEY">
    <xsl:copy>
      <xsl:copy-of select="@*|node()" />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="/">
    <xsl:element name="Result">
      <xsl:call-template name="ProcessItem">
        <xsl:with-param name="pItemsList" select="//ITEM" />
        <xsl:with-param name="pProcessedList" select="'|'" />
      </xsl:call-template>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

1 个答案:

答案 0 :(得分:0)

如果每个项目只有一个匹配或没有匹配,您可以尝试以下xslt:

<?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:strip-space elements="*"/>

    <xsl:key name="kItemNr" match="ITEM" use="@number" />
    <xsl:key name="kNumberKey" match="KEY" use="concat(../@number, '|', @name )" />

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>

        </xsl:template>

    <xsl:template match="ITEM">
        <xsl:if test="not(preceding::ITEM[@number = current()/@match])" >
            <Group>
                <xsl:attribute name="number">
                    <xsl:value-of select="@number"/>
                    <xsl:if test="@match" >
                        <xsl:text>,</xsl:text>
                        <xsl:value-of select="@match"/>
                    </xsl:if>
                </xsl:attribute>
                <xsl:variable name="itemNr" select="@number"/>
                <xsl:apply-templates select="KEY |  key('kItemNr',@match )/KEY[ 
                                     not (key('kNumberKey', concat($itemNr, '|', @name) ) )] ">
                    <xsl:sort select="@name"/>
                </xsl:apply-templates>

            </Group>
        </xsl:if>
    </xsl:template>
    <xsl:template match="/" >
        <Result>
            <xsl:for-each select="//ITEM[count(. |  key('kItemNr',number )  ) = 1 ]" >
                <xsl:apply-templates select="." />
            </xsl:for-each>
        </Result>
    </xsl:template>

</xsl:stylesheet>

将生成以下输出:

<?xml version="1.0"?>
<Result>
  <Group number="1"/>
  <Group number="2"/>
  <Group number="3,5">
    <KEY name="key1" value="x"/>
    <KEY name="key2" value="y"/>
    <KEY name="key3" value="z"/>
  </Group>
  <Group number="4"/>
  <Group number="6,10">
    <KEY name="key1" value="x"/>
    <KEY name="key2" value="y"/>
    <KEY name="key3" value="z"/>
    <KEY name="key4" value="a"/>
    <KEY name="key5" value="b"/>
  </Group>
  <Group number="7"/>
  <Group number="8">
    <KEY name="key1" value="x"/>
  </Group>
  <Group number="9"/>
</Result>

因更改请求而更新:

<?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:key name="kItemNr" match="ITEM" use="@number" />

    <xsl:template match="@*|node()">
        <xsl:copy >
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="ITEM">
        <xsl:variable name="matchStr" select=" concat(',', current()/@match, ',')"/>
        <xsl:if test="not(preceding::ITEM[ contains($matchStr, concat(',', @number, ',') )])" >
            <Group>
                <xsl:attribute name="number">
                    <xsl:value-of select="@number"/>
                    <xsl:if test="@match" >
                        <xsl:text>,</xsl:text>
                        <xsl:value-of select="@match"/>
                    </xsl:if>
                </xsl:attribute>

                <xsl:apply-templates select="(KEY | 
                                     //ITEM[ 
                                        contains( $matchStr, concat(',', @number, ',') )
                                    ]/KEY[
                                         not((preceding::ITEM[
                                             contains( $matchStr, concat(',', @number, ',') )
                                            ] | current() )/KEY/@name = @name)
                                     ]) ">
                    <xsl:sort select="@name"/>
                </xsl:apply-templates>

            </Group>

        </xsl:if>
    </xsl:template>
    <xsl:template match="/" >
        <Result>
            <xsl:for-each select="//ITEM[count(. |  key('kItemNr',number )  ) = 1 ]" >
                <xsl:apply-templates select="." />
            </xsl:for-each>
        </Result>

    </xsl:template>

</xsl:stylesheet>

对于更大的输入数据,这可能相当慢,但无论如何。