Xslt嵌套For-Loop中断

时间:2012-02-24 11:07:39

标签: xslt xslt-1.0

所有

我是XSLT的新手,面临着一个典型的问题。以下是问题详情

根据以下数据,我只需要在ANAME BBC而不是ItemB / tempBNum的一部分时显示ItemA / ANum元素

所以下面数据的结果应该是0214,BBC,因为它的部分是ItemA,也是BBC没有出现在ItemB中。

<myData>
<ItemA>
    <ANum>0213</ANum>
    <AName>OOC</AName>
</ItemA>
<ItemA>
    <ANum>0031</ANum>
    <AName>BBC</AName>
</ItemA>
<ItemA>
    <ANum>0214</ANum>
    <AName>BBC</AName>
</ItemA>
<ItemA>
    <ANum>0044</ANum>
    <AName>BBC</AName>
</ItemA>

<ItemB>
    <tempBNum>0031</tempBNum>
</ItemB>
<ItemB>
    <tempBNum>0044</tempBNum>
</ItemB>
</myData>

为此,我尝试使用嵌套For循环的逻辑,只有当ItemAObj / ANum = tempBNum时,嵌套循环的问题才会中断,但它不会破坏并产生重复的结果。

  <xsl:for-each select="myData/ItemA">
    <xsl:variable name="ItemAObj" select="." />
    Num = <xsl:value-of select="$ItemAObj/ANum"/>
    <xsl:for-each select="/myData/ItemB">
    <xsl:choose>
        <xsl:when test="$ItemAObj/ANum!=tempBNum and $ItemAObj/AName='BBC'"> <xsl:value-of select="tempBNum"/>
        <xsl:text> </xsl:text>
    </xsl:when>
        </xsl:choose>
        </xsl:for-each>
  </xsl:for-each>

对此的任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:0)

您无法休息,但添加了条件以确保没有previous-sibling ItemAObj/ANum = tempBNum

答案 1 :(得分:0)

这可以通过不同的方式实现,需要 for-each 迭代项目并在轮流上进行检查。相反,您可以使用键来查找数字是否存在 ItemB 元素

<xsl:key name="itemB" match="ItemB" use="tempBNum" />

然后,您可以简单地匹配 ANNA

中没有 ItemB 元素的 itemA 元素
<xsl:apply-templates select="ItemA[AName='BBC'][not(key('itemB', ANum))]" />

这是完整的XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" indent="yes"/>
   <xsl:key name="itemB" match="ItemB" use="tempBNum" />

   <xsl:template match="/myData">
      <xsl:apply-templates select="ItemA[AName='BBC'][not(key('itemB', ANum))]" />
   </xsl:template>

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

当应用于您的示例XML时,输出以下内容:

<ItemA>
   <ANum>0214</ANum>
   <AName>BBC</AName>
</ItemA>

答案 2 :(得分:0)

您无需使用任何xsl:for-each来解决问题,更不需要嵌套 xsl:for-each

事实上,可以使用单个XPath表达式选择所需的ItemA元素

/*/ItemA[AName = 'BBC' 
       and 
         not(ANum = /*/ItemB/tempBNum)
        ]

因此,评估上述XPath表达式并将结果复制到输出的简单XSLT转换是:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/">
     <xsl:copy-of select=
     "/*/ItemA[AName = 'BBC' and not(ANum = /*/ItemB/tempBNum)]"/>
 </xsl:template>
</xsl:stylesheet>

将此转换应用于提供的XML文档

<myData>
    <ItemA>
        <ANum>0213</ANum>
        <AName>OOC</AName>
    </ItemA>
    <ItemA>
        <ANum>0031</ANum>
        <AName>BBC</AName>
    </ItemA>
    <ItemA>
        <ANum>0214</ANum>
        <AName>BBC</AName>
    </ItemA>
    <ItemA>
        <ANum>0044</ANum>
        <AName>BBC</AName>
    </ItemA>
    <ItemB>
        <tempBNum>0031</tempBNum>
    </ItemB>
    <ItemB>
        <tempBNum>0044</tempBNum>
    </ItemB>
</myData>

产生了想要的正确结果

<ItemA>
   <ANum>0214</ANum>
   <AName>BBC</AName>
</ItemA>

如果我们只想要文本节点的值,只需使用string()函数:

string(/*/ItemA[AName = 'BBC' and not(ANum = /*/ItemB/tempBNum)])

然后是如此修改的转换:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
     <xsl:copy-of select=
     "string(/*/ItemA[AName = 'BBC' and not(ANum = /*/ItemB/tempBNum)])"/>
 </xsl:template>
</xsl:stylesheet>

当应用于同一XML文档(上面)时,产生:

    0214
    BBC

注意:如果性能开始变得重要,那么使用密钥可能是提高转换效率的下一步。