【问题标题】:Calling a xsl template with a different context调用具有不同上下文的 xsl 模板
【发布时间】:2015-10-15 16:13:00
【问题描述】:

我正在修改一个 XSL,它已经带有一些模板输出与当前上下文节点相关的数据。我想用不同的上下文调用相同的模板,这样我就不必通过传递额外的参数来更改现有的模板。

例如 XML:

<anyRoot>
 <level1>
     <a>xxxxxx</a>
     <b>yyyyyy</b>
     <level2>
         <a>aaaaa</a>
         <b>bbbbbb</b>
         <c>cccccc</c>
         <d>dddddd</d>
     </level2>
 </level1>
 <level1>
     <a>zzzzzz</a>
     <b>jjjjjj</b>
     <level2>
         <a>nnnnn</a>
         <b>bbbbbb</b>
         <c>cccccc</c>
         <d>dddddd</d>
     </level2>
 </level1>
</anyRoot>

理论 XSL。请注意,“context=”属性无效,但我把它放在那里是为了解释我的想法:

...
<xsl:for-each select="/anyRoot/level1/level2">
   <xsl:call-template name="testTmplate"/>
   <xsl:call-template name="testTmplate" context=".."/> <!-- passing parent of level2-->
</xsl:for-each>
...

<xsl:template name="testTmplate">
    <xsl:value-of select="./a"/>
</xsl:template>

这是我希望看到的输出:

aaaaa
xxxxxxx

nnnnnnn
zzzzzzz

【问题讨论】:

  • 您好,您尝试重用模板代码是对的!一些cmets:
  • 我希望第一个调用模板能够工作。第二个不起作用,但如果你用谷歌搜索祖先轴,你应该找到答案的方式。请尝试这些更改,看看效果如何。
  • 谢谢蒂姆。您的答案游戏是一个非常重要的线索,所以我最终为第二个模板调用(从父级提取数据的那个)添加了一个新模板。这个新模板充当包装模板来调用现有模板。请参阅下面的答案。

标签: xml xslt xsl-fo


【解决方案1】:

如果你想改变上下文,你真的应该在这里使用xsl:apply-templates,并带有一个匹配的模板。

例如

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

<xsl:template match="/">
<xsl:for-each select="/anyRoot/level1/level2">
   <xsl:apply-templates select="a" />
   <xsl:apply-templates select="../a" />
</xsl:for-each>
</xsl:template>

<xsl:template match="a">
    <xsl:value-of select="."/>
    <xsl:text>&#10;</xsl:text>
</xsl:template>
</xsl:stylesheet>

但是,如果您的实际 XSLT 中有另一个模板也匹配“a”元素,您可以通过使用 mode 属性来区分您需要的模板,如下所示:

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

<xsl:template match="/">
<xsl:for-each select="/anyRoot/level1/level2">
   <xsl:apply-templates select="a" mode="testTmplate" />
   <xsl:apply-templates select="../a" mode="testTmplate" />
</xsl:for-each>
</xsl:template>

<xsl:template match="a" mode="testTmplate">
    <xsl:value-of select="."/>
    <xsl:text>&#10;</xsl:text>
</xsl:template>
</xsl:stylesheet>

编辑:如果你真的想用这个方法调用现有的名称模板,只需从匹配的模板中调用它。试试这个...

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

<xsl:template match="/">
<xsl:for-each select="/anyRoot/level1/level2">
   <xsl:call-template name="testTmplate"/>
   <xsl:apply-templates select=".." mode="testTmplate" />
</xsl:for-each>
</xsl:template>

<xsl:template match="*" mode="testTmplate">
    <xsl:call-template name="testTmplate"/>
</xsl:template>

<xsl:template name="testTmplate">
    <xsl:value-of select="a"/>
    <xsl:text>&#10;</xsl:text>
</xsl:template>
</xsl:stylesheet>

【讨论】:

  • 如果我试图解析模板(还有更多模板)是按名称定义的,它不使用“匹配”,我不能使用“选择”来调用命名模板。有没有这种约束的解决方案?
  • 我认为您的解决方案是解决 XSL 限制的最佳解决方案。设计 XSL 的人应该认真考虑添加一个新的“context=”属性以避免所有这些“修复”。
  • 我已经修改了我的答案,以说明如果您真的想这样做,如何仍然可以使用您的命名模板。
  • XSL 命名模板不需要显式的“上下文”参数,因为当前上下文是隐式传递的。在问题中查看您对 call-template 的第一次调用,为什么会这样?因为当前上下文被传递给它。因此,要使用命名模板,您需要在调用之前更改上下文(如上面的解决方案)或添加显式参数以传递您要处理的节点集。
【解决方案2】:

不改变现有命名模板的一种方法是使用xsl:for-each 更改上下文:

<xsl:for-each select="/anyRoot/level1/level2">
   <xsl:call-template name="testTmplate"/>
   <xsl:for-each select=".."> <!-- parent of level2 -->
       <xsl:call-template name="testTmplate"/>
   </xsl:for-each>
</xsl:for-each>

我不愿推荐这个,因为 IMO,它不那么可读,但是用$context 乱扔许多模板可能最终也不那么可读。

【讨论】:

    【解决方案3】:

    原始./a 中的./ 是多余的,正如@tim-c 所说,使用xsl:apply-templates 可能会更好。但是,如果您不想过多地使用现有的 xsl:call-template 和命名模板设置,您可以添加一个默认为上下文节点的参数(例如,$context),然后根据需要覆盖它:

    <xsl:for-each select="/anyRoot/level1/level2">
       <xsl:call-template name="testTmplate"/>
       <xsl:call-template name="testTmplate">
           <xsl:param name="context" select=".."/> <!-- passing parent of level2-->
       </xsl:call-template>
    </xsl:for-each>
    ...
    
    <xsl:template name="testTmplate">
        <xsl:param name="context" select="."/>
        <xsl:value-of select="$context/a"/>
    </xsl:template>
    

    所以./a 变得比$context/a 更有用。

    【讨论】:

    • 谢谢。问题是,如果我为这个模板引入一个参数,我必须将参数的引入级联到从这个模板调用的所有其他模板中,而真正的XSL程序中有很多。
    【解决方案4】:

    下面走错路了!请参阅蒂姆的答案以了解正确的方法。但是,假设您已命名模板,您不能或不会更改并且您想重用它们,请参见下文。当然,根据您当前的代码,您可以向模板添加参数,或者最好还是使用带模式的未命名模板。

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
      <xsl:output method="text" />
    
      <xsl:template match="/">
        <xsl:for-each select="/anyRoot/level1/level2">
          <xsl:call-template name="testTmplate"/>
        </xsl:for-each>
        <xsl:for-each select="/anyRoot/level1">
          <xsl:call-template name="testTmplate"/>
        </xsl:for-each>
      </xsl:template>
    
      <xsl:template name="testTmplate">
        <xsl:value-of select="./a"/>
        <xsl:text>&#10;</xsl:text>
      </xsl:template>
    </xsl:stylesheet>
    

    【讨论】:

      【解决方案5】:

      谢谢蒂姆。您的答案游戏是一个非常重要的线索,所以我最终为第二个模板调用(从父级提取数据的那个)添加了一个新模板。这个新模板充当包装模板来调用现有模板。请参阅下面的答案。

      <?xml version="1.0" encoding="UTF-8"?>
      <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
          version="1.0">
          <xsl:output method="text" />
      
          <xsl:template match="/">
              <xsl:for-each select="/anyRoot/level1/level2">
                  <xsl:call-template name="testTemplate" />
                  <xsl:apply-templates select=".." mode="testTemplateWrapper" />
              </xsl:for-each>
          </xsl:template>
      
          <xsl:template name="testTemplate">
              <xsl:value-of select="a"/>
              <xsl:text>&#10;</xsl:text>
          </xsl:template>
      
          <xsl:template match="level1" mode="testTemplateWrapper">
              <xsl:call-template name="testTemplate" />
          </xsl:template>
      
      </xsl:stylesheet>
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-09-03
        • 2021-07-11
        • 2015-05-02
        • 1970-01-01
        • 1970-01-01
        • 2021-09-29
        • 2018-05-27
        相关资源
        最近更新 更多