使用XSLT Apply-Templates有条件地选择节点

时间:2010-10-20 21:03:08

标签: xml xslt xpath

假设我有一个像这样的xml文档:

<director>
    <play>
        <t>Nutcracker</t>
        <a>Tom Cruise</a>
    </play>
    <play>
        <t>Nutcracker</t>
        <a>Robin Williams</a>
    </play>
    <play>
        <t>Grinch Stole Christmas</t>
        <a>Will Smith</a>
    </play>
    <play>
        <t>Grinch Stole Christmas</t>
        <a>Mel Gibson</a>
    </play>
</director>

现在我希望能够选择威尔·史密斯作为演员的所有戏剧并将其重新格式化为以下内容:

<Plays>
    <Play title="Grinch Stole Christmas">
       <star>Will Smith</star>
       <star>Mel Gibson</star>
    </Play>
</Plays>

我只想使用apply-templates ..没有xsl:if或者每个循环(我把这个例子设想为我正在做的更简单的版本,所以你可以帮我理解如何在匹配中使用xpath语句)

这是我到目前为止所做的:

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" />
        <xsl:template match="/director">
                <Plays>
                <xsl:apply-templates select="play"/>
                </Plays>
        </xsl:template>

        <xsl:template match="play[a='Will Smith']">
                <play title="{data(t)[1]}">
                <xsl:apply-templates select="a"/>
                </play>
        </xsl:template>

        <xsl:template match="a">
                <star>
                <xsl:value-of select="."/>
                </star>
        </xsl:template>
</xsl:stylesheet>

基本上我只是不确定如何在模板的match属性中使用XPath过滤掉节点。任何帮助都会很棒!

3 个答案:

答案 0 :(得分:9)

条件应该是xsl:apply-templates而不是xsl:template:

<Plays>
  <xsl:apply-templates select="play[a='Will Smith']">"/>
</Plays>

在您的解决方案中,您正在改变所有&lt; play&gt;节点。 对于与条件匹配的播放节点,将应用您的模板。但对于那些与条件不匹配的人,则应用默认模板(“身份变换”)。

或者,您可以将条件保持在xsl:template match,但为&lt; play&gt;添加另一个模板与条件不匹配,转换那些&lt; play&gt;什么都没有:

    <xsl:template match="play[a='Will Smith']">
      <play title="{data(t)[1]}">
        <xsl:apply-templates select="a"/>
      </play>
    </xsl:template>

    <xsl:template match="play">
    </xsl:template>

答案 1 :(得分:5)

<强>予。可能是效率最高的XSLT 1.0解决方案:

<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:key name="kWSPlayByTitle" match="play[a='Will Smith']"
  use="t"/>

 <xsl:key name="kActorByTitle" match="a"
  use="../t"/>

 <xsl:template match="/">
  <Plays>
    <xsl:apply-templates select=
    "*/play[generate-id()
           =
            generate-id(key('kWSPlayByTitle',t)[1])
           ]"/>
  </Plays>
 </xsl:template>

 <xsl:template match="play">
  <Play title="{t}">
   <xsl:apply-templates select="key('kActorByTitle',t)"/>
  </Play>
 </xsl:template>

 <xsl:template match="a">
  <star><xsl:value-of select="."/></star>
 </xsl:template>
</xsl:stylesheet>

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

<director>
    <play>
        <t>Nutcracker</t>
        <a>Tom Cruise</a>
    </play>
    <play>
        <t>Nutcracker</t>
        <a>Robin Williams</a>
    </play>
    <play>
        <t>Grinch Stole Christmas</t>
        <a>Will Smith</a>
    </play>
    <play>
        <t>Grinch Stole Christmas</t>
        <a>Mel Gibson</a>
    </play>
</director>

生成了想要的结果

<Plays>
   <Play title="Grinch Stole Christmas">
      <star>Will Smith</star>
      <star>Mel Gibson</star>
   </Play>
</Plays>

请注意

  1. 通过使用按键来实现效率,对于Mell Gibson参与的所有游戏以及参与某个(标题)游戏的所有演员都可以。

  2. 即使梅尔吉布森的比赛名称被多次列出(由于意外错误,也许......),结果中只会列出一次

  3. <强> II。一个简单而有效的XSLT 2.0解决方案:

    <xsl:stylesheet version="2.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
    
     <xsl:template match="/*">
      <Plays>
        <xsl:for-each-group select="play[a='Mel Gibson']"
              group-by="t">
          <xsl:apply-templates select="."/>
        </xsl:for-each-group>
      </Plays>
     </xsl:template>
    
     <xsl:template match="play">
      <Play title="{t}">
       <xsl:for-each-group select="../play[t = current()/t]/a"
            group-by=".">
         <xsl:apply-templates select="."/>
       </xsl:for-each-group>
      </Play>
     </xsl:template>
    
     <xsl:template match="a">
      <star>
        <xsl:value-of select="."/>
      </star>
     </xsl:template>
    </xsl:stylesheet>
    

答案 2 :(得分:1)

此样式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kActorByTitle" match="a" use="../t"/>
    <xsl:param name="pActor" select="'Will Smith'"/>
    <xsl:template match="/">
        <Plays>
            <xsl:apply-templates select="*/play[a=$pActor]"/>
        </Plays>
    </xsl:template>
    <xsl:template match="play">
        <Play title="{t}">
            <xsl:apply-templates select="key('kActorByTitle',t)"/>
        </Play>
    </xsl:template>
    <xsl:template match="a">
        <star>
            <xsl:value-of select="."/>
        </star>
    </xsl:template>
</xsl:stylesheet>

输出:

<Plays>
    <Play title="Grinch Stole Christmas">
        <star>Will Smith</star>
        <star>Mel Gibson</star>
    </Play>
</Plays>