试图避免xsl:for-each

时间:2015-03-30 15:02:52

标签: xml xslt

假设我有一个像这样的xml.document(不完整):

<root>
<elem id="1"/>
<elem id="2"/>
<elem id="3"/>
</root>

我通过doc-function访问它,如下所示:

<xsl:variable name="curDoc" select="doc(iri-to-uri("abc.xml"))"/>

现在我想将应用样式表的文档中的每个元素与该abc.xml中的每个元素进行比较。

我应用样式表的文档如下所示:

<node>
<somenode id="1"/>
<somenode id="2"/>
<somenode id="3"/>
</node>

现在两个文件中的元素实际上都没有按顺序排列!

还有另外一种方法可以做到这一点:

<xsl:template match="somenode">
<xsl:variable name="curElem" select="."/>
<xsl:for-each select="$curDoc/root/elem">
<xsl:if test="@id = $curElem[@id]">
<!-- do something -->
<xsl:if>
</xsl:for-each>
</xsl:template>

我读了很多你应该避免的 - 并且只是使用模板机制,但是我怎么能在没有for-each的情况下这样做(使用XSLT 2.0)?

4 个答案:

答案 0 :(得分:2)

将条件移至<for-each>选择。

<xsl:template match="somenode">
  <xsl:variable name="currentElementId" select="./@id"/>
  <xsl:for-each select="$curDoc/root/elem[@id = $currentElementId]">
    <!-- do something -->
  </xsl:for-each>
</xsl:template>

此解决方案仍然使用循环。这是正确的,因为节点列表可能为空,或者您可以使用具有相同elem属性值的多个id元素。

在XML中,属性id不一定是唯一的,它取决于文档定义(DTD,XSD,...)。

大多数函数使用列表中的第一个节点,因此它应该在没有for-each的情况下工作。实际上。

<xsl:template match="somenode">
  <xsl:variable name="currentElementId" select="./@id"/>
  <xsl:variable name="targetNode" select="$curDoc/root/elem[@id = $currentElementId]">
  <xsl:if test="$targetNode">
    <!-- do something with $targetNode -->
  </xsl:if>
</xsl:template>

答案 1 :(得分:2)

xsl:for-each本质上不是邪恶的。只是过度使用xsl:for-each(和xsl:if)通常是程序员的一个症状,他们没有掌握语言中更强大的结构。

查看代码,实际上是在进行连接,在XSLT中进行连接的最佳方法是使用密钥。所以我会把它写成

<xsl:key name="id" match="elem" use="@id"/>

<xsl:template match="somenode">
  <xsl:for-each select="key('id', @id, $curdoc)">
    <!-- do something -->
  </xsl:for-each>
</xsl:template>

您可以使用xsl:apply-templates替换xsl:for-each,也许是在特定模式下,但除非有某些原因,否则我不会这样做。期望有人可能希望有一天能代替你做某事&#34;做某事&#34;用&#34;做些不同的事情&#34;。

答案 2 :(得分:0)

以下是一个小例子,您可以在不使用<xsl:for-each>指令的情况下完成所需操作:

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

 <xsl:variable name="vDoc">
        <root>
            <elem id="1"/>
            <elem id="2"/>
            <elem id="3"/>
        </root> 
 </xsl:variable>

  <xsl:template match="somenode">
    <xsl:apply-templates select="$vDoc/*/elem[@id eq current()/@id]">
      <xsl:with-param name="porigElem" select="."/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="elem">
    <xsl:param name="porigElem" as="element()"/>
    Match:
      Original: <xsl:sequence select="$porigElem"/>
      Matching: <xsl:sequence select="."/>
  </xsl:template>
</xsl:stylesheet>

在提供的源XML文档上应用此转换(重新排序,以匹配问题的描述):

<node>
    <somenode id="3"/>
    <somenode id="1"/>
    <somenode id="2"/>
</node>

产生了想要的正确结果:

Match:
  Original: <somenode id="3"/>
  Matching: <elem id="3"/>

Match:
  Original: <somenode id="1"/>
  Matching: <elem id="1"/>

Match:
  Original: <somenode id="2"/>
  Matching: <elem id="2"/>

当然,如果想按照凯博士的回答中的建议使用密钥,必须定义密钥,然后模板的应用将如下所示:

<xsl:apply-templates select="key('kelemById', @id, $vDoc)">
  <xsl:with-param name="porigElem" select="."/>
</xsl:apply-templates>

现在完全转型是:

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

 <xsl:key name="kelemById" match="elem" use="@id"/>

 <xsl:variable name="vDoc">
        <root>
            <elem id="1"/>
            <elem id="2"/>
            <elem id="3"/>
        </root> 
 </xsl:variable>

  <xsl:template match="somenode">
    <xsl:apply-templates select="key('kelemById', @id, $vDoc)">
      <xsl:with-param name="porigElem" select="."/>
    </xsl:apply-templates>
  </xsl:template>

  <xsl:template match="elem">
    <xsl:param name="porigElem" as="element()"/>
    Match:
      Original: <xsl:sequence select="$porigElem"/>
      Matching: <xsl:sequence select="."/>
  </xsl:template>
</xsl:stylesheet>

当此转换应用于同一源XML文档时,会产生相同的想要和正确的结果。

答案 3 :(得分:-1)

我不确定这是否更好,但这应该有效:

<xsl:stylesheet version="1.0"
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

  <xsl:template match="somenode">
     <xsl:variable name="curElem" select="."/>
     <xsl:apply-templates select="$curDoc" />
  </xsl:template>

  <xsl:template match="root/elem">
        <xsl:if test="@id = $curElem[@id]">
           test
        </xsl:if>
  </xsl:template>