2013-04-23 76 views
2

在我的阅读中,很明显XSLT 1.0中对for i .. m循环的唯一理智解决方法是使用递归模板。XSLT循环或替代节点复制

除非有人能够解释,否则,进一步看来,鉴于XSLT处理的限制,这种方法通常不是可重用的。

无论如何,给出的输入代码段(在这种情况下上下文节点):

<items count="3"> 
    <item> 
     <name>Name</name> 
     <description>Description</description> 
    </item> 
</items> 

是否有重复基础上count属性<item>孩子一个可重复使用的策略是什么?这里的预期产出将仅仅是

<item> 
    <name>Name</name> 
    <description>Description</description> 
</item> 
<item> 
    <name>Name</name> 
    <description>Description</description> 
</item> 
<item> 
    <name>Name</name> 
    <description>Description</description> 
</item> 

我打算在<item>节点上执行进一步的转变,但我不认为他们是相关的。

可重用性是我关心的一个问题,原因很简单,因为count属性在输入文档的元素中非常常见,而语义意图就如上面我的示例所述。如果我要使用递归迭代器的方法,我不得不将它烘焙到每个模板中(,这将非常干;更像是非常湿,如“为什么甚至尝试”;但我离题了

如果有一项战略,以创建一个通用模板for,与任何变换操作都不能进行,那将只是壮观。如果我不使用任何递归迭代器就可以逃脱,如果为此目的使用的某个函数被放在XSLT 1.0中,那也同样壮观。

无论如何,我该如何做到这一点?我是否需要诉诸WET方法,还是有更好的方法?

回答

3

递归没有错。应该很容易使其足够通用于这种情况。

下面是一个例子。除了输出子元素相同数量count,我也改变了name元素new_elem(只是为了显示额外的转换)...

XSLT 1.0

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

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

    <xsl:template match="*[@count]"> 
     <xsl:apply-templates select="*" mode="dupe"> 
      <xsl:with-param name="count" select="@count"/> 
     </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template match="*" mode="dupe"> 
     <xsl:param name="count"/> 
     <xsl:apply-templates select="."/> 
     <xsl:if test="$count > 1"> 
      <xsl:apply-templates mode="dupe" select="."> 
       <xsl:with-param name="count" select="$count - 1"/> 
      </xsl:apply-templates> 
     </xsl:if> 
    </xsl:template> 

    <xsl:template match="name"> 
     <new_elem><xsl:value-of select="."/></new_elem> 
    </xsl:template> 

</xsl:stylesheet> 

输出(使用来自问题的输入)

<item> 
    <new_elem>Name</new_elem> 
    <description>Description</description> 
</item> 
<item> 
    <new_elem>Name</new_elem> 
    <description>Description</description> 
</item> 
<item> 
    <new_elem>Name</new_elem> 
    <description>Description</description> 
</item> 

如果你可以使用XSLT 2.0,你可以像你所说的迭代在您的问题开始时:

XSLT 2.0(产生与上述相同的输出。如果有)的*[@count]超过一个子元素

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

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

    <xsl:template match="*[@count]"> 
     <xsl:variable name="curr" select="."/> 
     <xsl:for-each select="1 to @count"> 
      <xsl:apply-templates select="$curr/*"/> 
     </xsl:for-each> 
    </xsl:template> 

    <xsl:template match="name"> 
     <new_elem><xsl:value-of select="."/></new_elem> 
    </xsl:template> 

</xsl:stylesheet> 
+0

您可以通过将'select'换行到'current()[$ count> 1]''后消除'xsl:if'。当前的方法还有一个可能的缺点,那就是它将复制包含节点的单个元素而不是整个内容。例如''会导致'',而不是''。您可以通过将第二个模板的第一行修改为'select =“。”',并从第三个模板的第二行中删除“select”来更改此选项。 – JLRishe 2013-04-23 20:39:33

+0

好用于两个版本的循环编码+1。 – 2014-12-20 06:05:45

2

如果有一个战略,以创建一个通用模板,与 任何转换操作都不能进行,这将是这将是略有不同只是 壮观。如果我可以在不使用任何递归迭代器的情况下离开,如果用于此目的的函数的某些gem被隐藏在XSLT 1.0, 中,那将同样壮观。

无论如何,我该如何做到这一点?我是否需要诉诸WET 的方法,还是有更好的方法?

的希望DRY岬是FXSL的模板/功能iter的琐碎应用:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:f="http://fxsl.sf.net/" xmlns:myRepeat="f:myRepeat" 
exclude-result-prefixes="xsl f myRepeat"> 
<xsl:import href="iter.xsl"/> 

<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

    <myRepeat:myRepeat/> 
    <xsl:variable name="vFunRepeat" select="document('')/*/myRepeat:*[1]"/> 

    <xsl:variable name="vAdditive" select="/*/*[1]"/> 

    <xsl:template match="/*"> 
     <xsl:call-template name="iter"> 
      <xsl:with-param name="pTimes" select="@count"/> 
      <xsl:with-param name="pFun" select="$vFunRepeat"/> 
      <xsl:with-param name="pX" select="/.."/> 
     </xsl:call-template> 
    </xsl:template> 

    <xsl:template match="myRepeat:*" mode="f:FXSL"> 
    <xsl:param name="arg1"/> 

    <xsl:copy-of select="$arg1 | $vAdditive"/> 
    </xsl:template> 
</xsl:stylesheet> 

当这种变换所提供的XML文档应用:

<items count="3"> 
    <item> 
     <name>Name</name> 
     <description>Description</description> 
    </item> 
</items> 

想要的,正确的resulul t的生产:

<item> 
    <name>Name</name> 
    <description>Description</description> 
</item> 
<item> 
    <name>Name</name> 
    <description>Description</description> 
</item> 
<item> 
    <name>Name</name> 
    <description>Description</description> 
</item> 

请注意

  1. 你没有写任何递归模板可言。

  2. iter模板与大多数其他FXSL模板一样非常通用且功能强大,无需程序员一次又一次地编写和调试递归。

  3. FXSL提供想要干的烦躁和刺激思维更抽象 - 从而更强大的结构 - 功能,折叠,迭代,......等等

  4. 它发生,使这个问题有XSLT 2.0中的一个简单解决方案(<xsl:for-each select="1 to @count">),但还有很多其他问题,其XSLT 2.0中的解决方案并不那么直截了当。 FXSL帮助使用的最普遍和强大的高阶函数的通用和强大的执行方式解决任何这样的“难”的问题 - 褶皱,地图,扫描,拉链,......等等


二,使用Piez method(当存在用于的@count值一个已知的上限)

<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:variable name="vStyle" select="document('')"/> 
<xsl:variable name="vNodes" select= 
    "$vStyle//node()|$vStyle//@* | $vStyle//namespace::*"/> 

<xsl:variable name="vAdditive" select="/*/*[1]"/> 

<xsl:template match="/*"> 
    <xsl:for-each select="$vNodes[not(position() > current()/@count)]"> 
     <xsl:copy-of select="$vAdditive"/> 
    </xsl:for-each> 
</xsl:template> 
</xsl:stylesheet> 

当这种转化是在同一个XML文档(以上)应用中,相同的正确的结果产生

<item> 
    <name>Name</name> 
    <description>Description</description> 
</item> 
<item> 
    <name>Name</name> 
    <description>Description</description> 
</item> 
<item> 
    <name>Name</name> 
    <description>Description</description> 
</item> 

请注意

当Piez方法CA n被使用,递归完全避免。

免责声明

我很高兴11-12年前开发FXSL,我已经提到几千从那时起次,其中包括两个会议论文,不知道我应该提供一个免责声明:)