可以更好地控制xsl / xpath中的for-each吗?

时间:2010-06-25 16:34:24

标签: xml xslt xpath xsd

嘿朋友们 我有另一个xsl / xpath问题。确实是一个初学者问题,但我认为仍然太“必要”。

<!-- XML -->
<Module>
    <WideTeaserElement>
      <Headline>Bla bla 1</Headline>
    </WideTeaserElement>
    <WideTeaserElement>
      <Headline>Bla bla 2</Headline>
    </WideTeaserElement>
</Module>

<!-- XSL -->
<!-- You are already in the Node Module -->
<xsl:template match="WideTeaserElement">
    <ul class="TabbedPanelsTabGroup">
        <xsl:for-each select=".">
            <li class="TabbedPanelsTab"><a href="#"><xsl:value-of select="pub:Headline"/></a></li>
         </xsl:for-each>
    </ul>
</xsl>

我想得到的是输出:

<ul class="TabbedPanelsTabGroup">
     <li class="TabbedPanelsTab"><a href="#">Bla bla 1</a></li>
     <li class="TabbedPanelsTab"><a href="#">Bla bla 2</a></li>
</ul>

使用我的XSL,我得到一个带有两个<ul>元素的输出。所以我的问题包括两件事: 1)如何改变我的问题? 2)如何在XSL中处理像for-each这样的循环,让它更“受控制”?

3 个答案:

答案 0 :(得分:2)

你几乎从不需要在XSL中使用for-each,如果你发现自己使用它,你可能会错过XSL的重要功能。

通过为HeadLine元素(包含<li>...</li>元素)提供模板,然后在<xsl:apply-templates/>元素中使用<ul>,可以更恰当地表达您的案例在WideTeaserElement模板中。

<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:template match='Module'>
    <ul class='TabbedPanelsTabGroup'>
      <xsl:apply-templates/>
    </ul>
  </xsl:template>
  <xsl:template match='Headline'>
    <li class='TabbedPanelsTab'><a href='#'><xsl:apply-templates/></a></li>
  </xsl:template>
</xsl:stylesheet>

答案 1 :(得分:2)

这种转变(完全符合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="Module">
   <ul>
     <xsl:apply-templates/>
   </ul>
 </xsl:template>

 <xsl:template match="Headline">
   <li class="TabbedPanelsTab"><a href="#"><xsl:value-of select="."/></a></li>
 </xsl:template>
</xsl:stylesheet>

应用于提供的XML文档

<Module>
    <WideTeaserElement>
      <Headline>Bla bla 1</Headline>
    </WideTeaserElement>
    <WideTeaserElement>
      <Headline>Bla bla 2</Headline>
    </WideTeaserElement>
</Module>

产生所需的结果

<ul>
    <li class="TabbedPanelsTab">
        <a href="#">Bla bla 1</a>
    </li>
    <li class="TabbedPanelsTab">
        <a href="#">Bla bla 2</a>
    </li>
</ul>

答案 2 :(得分:1)

您获得两个ul元素的原因是因为生成ul元素的模板与名为WideTeaserElement的元素匹配,其中有两个元素。

我想我可以看到思考与此有何关系 - 你是否假设这个模板将在一个操作中处理所有WideTeaserElement,然后你可以用'为每个'进行迭代?相反,每次出现WideTeaserElement节点时都会单独调用模板,然后通过单次出现来“迭代”。

我同意诺曼的观点,for-each很少是最佳选择,但我可以想到你可能会使用它的两个原因。

  • 可读性;如果每次迭代的处理量相对较小,那么如果处理它的代码在父模板中,它可以使样式表更容易阅读。

对于(非常简化的)例子,我赞成

 <xsl:template match="mylist">
   <xsl:element name="ul">
     <xsl:for-each select="listitem">
       <xsl:element name="li">
         <xsl:value-of select="." />
       </xsl:element>
     </xsl:for-each>
   </xsl:element>
 </xsl:template>

而不是

<xsl:template match="mylist">
  <xsl:element name="ul">
    <xsl:apply-templates select="listitem" />
  </xsl:element>
</xsl:template>

<xsl:template match="listitem">
  <xsl:element name="li">
    <xsl:value-of select="." />
  </xsl:element>
</xsl:template>

如果mylist模板实际上有更多代码,后一种解决方案意味着必须向下滚动我的代码才能看到listitem的处理方式。这是主观的,有些人可能总是喜欢后一种解决方案。就个人而言,我通常发现大多数模板足够大,破坏它们对于可读性更好,但在较小的模板中并非总是这样。

  • 您不希望通过模板正常处理迭代的节点。在上面的示例中,后一种解决方案将转换所有listitem标记,而不仅仅是'mylist'标记内的标记。当然可以通过将匹配更改为listitem[parent::mylist]来限制它,但for-each通常比基于祖先为同一元素名称制作多个模板更简洁。

一般来说,您通常可以替换

<xsl:template match="foo">
  <xsl:for-each select="bar">
    ..
  </xsl:for-each>
</xsl:template>

<xsl:template match="foo">
  <xsl:apply-templates select="bar" />
</xsl:template>

<xsl:template match="bar">
  ..
</xsl:template>

bar元素始终具有foo元素的任何文档中,作为其父元素。